EB3I n1 2025 scRNAseq
-
Differential expression analysis




# Start Rstudio
- Using the OpenOnDemand/Rstudio cheat sheet, connect to the OpenOnDemand portal and create a Rstudio session with the right resource requirements.
# Warm-up
- We set common parameters we will use throughout this session :

1 Prepare the data structure

We will do the same as for former steps, just changing the session name :

1.1 Main directory

1.2 Current session

1.3 Input directory

1.4 Output directory



2 Reload the Seurat Object

  • We can reload the object we saved at the former step

3 Forewords

I need three teams for this session: - Team Wilcoxon - Team Student - Team [MAST]

I will also need your help because I can’t make DESeq2 work correctly.

But I’m sure, that we will solve my issue: you’re in the best session here at EB3I.

3.1 TLDR: R command lines

In this presentation, there will be screen captures for you to follow the lesson. There will also be every single R command lines.

Do not take care of the command lines if you find them too challenging.

Our goal here, is to understand the main mechanism of Differential Expression Analysis. R is just a tool.

Below are the libraries we need to perform this whole session:

base::library(package = "BiocParallel")    # Optionally multithread some steps
base::library(package = "DT")              # Display nice tables in HTML
base::library(package = "ggplot2")         # Draw nice graphs and plots
base::library(package = "ggpubr")          # Draw even nicer graphs
base::library(package = "rstatix")         # Base R statistics
base::library(package = "knitr")           # Build this presentation
base::library(package = "dplyr")           # Handle big tables
base::library(package = "Seurat")          # Handle SingleCell analyses
base::library(package = "SeuratObject")    # Handle SingleCell objects for Seurat
base::library(package = "SingleCellExperiment") # Handle SingleCell file formats
base::library(package = "UpSetR")          # Nice venn-like graphs
base::library(package = "EnhancedVolcano") # Draw Volcano plot

Then we join layers:

# join_layers_tldr

Seurat::Idents(sobj) <- sobj$orig.ident
sobj <- SeuratObject::JoinLayers(sobj)

Then we perform differential expression analysis:

# run_dea

sobj_de <- Seurat::FindMarkers(
  # Object on which to perform DEA
  object = sobj,
  # Name of factor in condition 1
  ident.1 = "TD3A",
  # Name of factor in condition 2
  ident.2 = "TDCT"
)

And that’s all ! Our goal is to understand these lines, being able to write them is a bonus.

3.2 Purpose of this session

Up to now, we have:

  1. Identified to which cell each sequenced reads come from
  2. Identified to which gene each read come from
  3. Identified possible bias in gene expression for each cell
  4. Filtered and corrected these bias as well as we can

We would like to identify the list of genes that characterize differences between cell cycle phases G1 and S groups.

At the end of this session you will know:

  1. how to select a differential analysis method
  2. how to select the correct normalization (if any?) that must be provided to your differential analysis method
  3. How to read differential expression results

3.3 Insight

We are wondering what’s in our dataset. Let’s have a look, with the function print form the package base.

# print_seurat

base::print(
  # Object to display
  x = sobj
)
Show output
An object of class Seurat 
12926 features across 3886 samples within 1 assay 
Active assay: RNA (12926 features, 2000 variable features)
 3 layers present: data, counts, scale.data
 6 dimensional reductions calculated: pca, CCAIntegration, umap, RPCAIntegration, HarmonyIntegration, HarmonyStandalone

We have 0 features (aka genes), across 0 samples (aka cells) in the TD3A condition. We have 0 features (aka genes), across 0 samples (aka cells) in the TD3A condition.

Let us have a look at the RNA counts for 10 cells and their annotation, with the function head from the package utils.

# head_seurat_counts_phase

utils::head(
  # Object to visualize
  x = sobj,
  # Number of lines to display
  n = 10
)

Prettier with a table in HTML :

There are no counts, normalized or not !!

Where are they ?

In order to explore the content of sobj, use the function str from the package utils:

# str_seurat_object

utils::str(
  # Object to explore
  object = sobj@assays
)

Alternatively, in RStudio, you can click on the object pane and explore manually the content of the object. If we explore the slot assays, then we find the counts.

You can access them with:

# head_seurat_count_table

utils::head(
  # Object to visualize
  x = SeuratObject::GetAssayData(object = sobj, assay = "RNA", layer = "counts"),
  # Number of rows to display
  n = 10

  )

Prettier with a table in HTML :

We have one gene per line, one cell per column, and RNA counts in each row.

For the sake of this session, we won’t compare the whole dataset. It would take up to 15 minutes to complete. During the rest of this session, we will compare the Louvain clusters 8 and 10, from our integration with Harmony.

We need to re-annotate layers to do so. This is done in two steps:

  1. Redefine Idents in order to be able to call cells by their cluster names.
  2. JoinLayers to have a single count table with both TD3A and TDCT cells from clusters 8 and 10 together.
# ident_and_join_layers

Seurat::Idents(sobj) <- sobj$HarmonyStandalone_clusters
sobj <- SeuratObject::JoinLayers(sobj)

Let’s check if our cells are joint:

# check_joint_layers

base::print(sobj)
Show output
An object of class Seurat 
12926 features across 3886 samples within 1 assay 
Active assay: RNA (12926 features, 2000 variable features)
 3 layers present: data, counts, scale.data
 6 dimensional reductions calculated: pca, CCAIntegration, umap, RPCAIntegration, HarmonyIntegration, HarmonyStandalone

Question :

  • We have one gene per line, one cell per column, and RNA counts in each row.

    # q_normscalfilt
    
    ## Are these counts normalized ? scaled ? filtered ?


    # a_normscalfilt
    
    ## . These counts are normalized, scaled, filtered for sure.
    ##
    ## . This information is available in the seurat object itself, 
    ##   within the slot `@commands`. 

    See an example below:

    # a_normscalfilt
    
    ## . These counts are normalized, scaled, filtered for sure.
    ##
    ## . Total counts, max value, counts per cell and number of
    ##   expressed features per cell, all have decreased.
    ##
    ## . This information is available in the seurat object itself, 
    ##   within the slot `@commands`. See an example below:
    # seurat_history
    
    names(sobj@commands)

    Show output

     [1] "NormalizeData.RNA"                   
     [2] "FindVariableFeatures.RNA"            
     [3] "ScaleData.RNA"                       
     [4] "RunPCA.RNA"                          
     [5] "RunUMAP.RNA.CCAIntegration"          
     [6] "FindNeighbors.RNA.CCAIntegration"    
     [7] "RunUMAP.RNA.RPCAIntegration"         
     [8] "FindNeighbors.RNA.RPCAIntegration"   
     [9] "RunUMAP.RNA.HarmonyIntegration"      
    [10] "FindNeighbors.RNA.HarmonyIntegration"
    [11] "RunUMAP.RNA.HarmonyStandalone"       
    [12] "FindNeighbors.RNA.HarmonyStandalone" 
    [13] "FindClusters"                        

    However, please remember that :

    • Counts in the slot count are raw counts
    • Normalized counts are in the slot data
    • scaled data are in the slot scaled.data (this one was damn obvious).

    And it you do not find that crystal-clear, I totally agree with you.


  • Raw counts have many, so many zeros …

    # q_sozero
    
    ## Is it normal to you ?


    # a_sozero
    
    ## . The large number of null counts is completely normal for this
    ##   "droplet" technology. In maths/stats, we talk about "matrix sparcity",
    ##   i.e a table with lots (lots!) of zeros.


  • And count values are so low …

    # q_lowcounts
    
    ## Why ? Is it because we downsampled the read depth for this training ?


    # a_lowcounts
    
    ## . No. Once again, this is intrinsic to the technology used.
    ##
    ## . If the data were to be downsampled, we would had done this by cropping
    ##   the count matrix over a small chromosome, or a reduced gene est, not 
    ## by lowering the global amount of reads (depth).

4 Select a DE method

4.1 Available methods

Seurat let us use multiple differential analysis methods with its function FindMarkers.

  1. wilcox: The wilcoxon test tests the mean of expression and looks for a difference in these means.
  2. MAST: This tool has been built for Single Cell. It is based on a statistical model called “Hurdle Model”, which excells with data that contains lots of zeros (which is our case in Single Cell RNA-Seq: most of the genes are not expressed.)
  3. DESeq2: This tool has originally been built for bulk RNA-Seq but now includes specific functions for Single Cell. It performs well when counts are highly variable or when you wand to compare a handful of cells.
  4. t-test: The t-test uses a comparison of means to assess differential expression probability.
  5. roc: In this method, an AUC value of 1 means that expression values for this gene alone can perfectly classify the two groupings, i.e : each of the cells in “cells.1” (condition 1) exhibit a higher level than each of the cells in “cells.2” (condition 2). An AUC value of 0 also means there is perfect classification, but in the opposite direction (higher in condition 2 than in 1). A value of 0.5 implies that the gene has no predictive power to classify the two groups.

The main question now is how to choose the right test ??

Spoilers : there are no option better than another in all ways ! Thank you for your attention, you can go back home now.


From Soneson & Robinson (2018) Nature Methods:

de_tools

Here, researchers have designed an artificial dataset where they knew in advance the list of differentially expressed genes. They have used all these algorithms and consigned the results.

  1. DESeq2, Limma seem to have a higher number of false positives (genes identified as differentially expressed while they were actually not.)
  2. Wilcoxon seems to be better in general.
  3. MAST performs well in absolute

ANOVA was not present in this study (but probably would have performed similarly to t-test.

4.2 A case: the Plac8 gene

The question is now to guess whether this gene is differentially expressed, or not.

4.2.1 Cell observations

Let’s have a look at the gene named ‘Plac8’

It is known to be involved in positive regulation of cold-induced thermogenesis, and positive regulation of transcription by RNA polymerase II.

In order to plot its expression across all cells, we are going to use the function VlnPlot from the Seurat package.

The input object is obviously contained in the sobj variable, since it is our only Seurat object. In addition, we are going to select the feature Plac8, and split the graph according to the clusters we annotated earlier.

# seurat_vlnplot_Plac8_demo

Seurat::VlnPlot(
  # A subset of the Seurat object
  # limited to clusters 8 and 10, 
  # or else we will plot all the clusters
  object = subset(sobj, HarmonyStandalone_clusters %in% c("8", "10")),
  
  # The name of the gene of interest (feature = gene)
  features = "Plac8",
  
  # The name of the Seurat cell annotation
  split.by = "HarmonyStandalone_clusters",
  
  # Change color for presentation
  cols = c("darkslategray3", "olivedrab")
  
)
Show plot

# q_isplac8de

## Using your intuition, is this gene differentially expressed 
## between clusters 8 and 10 ?
# q_isplac8de

## . In cluster 10, the violin plot highlights almost no cells with low or 
##   zero `Plac8` expression. The highest density of cells has a `Plac8` 
##   normalized expression aroung ~1.5.
##
## . In cluster 8, cells seem to have no expression of `Plac8` at all.
##
## . IMHO (feel free to disagree), the expression of the gene `Plac8` differs 
## between our clusters 8 and 10. This is purely intuitive.


Now, let’s check the same gene expression, but comparing cells tagged as in the G1 and S phase.

# vlnplot_seurat_group_phase_code

Seurat::VlnPlot(
  # The Seurat object
  object = sobj,
  # The name of the gene of interest (feature = gene)
  features = "Plac8",
  # The name of the Seurat cell-cycle annotation
  group.by = "CC_Seurat_Phase",
  # Change color for presentation
  cols = c("darkslategray3", "olivedrab", "orangered3")
)
Show plot

# q_isplac8deG1S

## Using your intuition, is this gene differentially expressed 
## between the G1 and S cell cycle phases ?
# a_isplac8deG1S

## . This one seems more tricky... One can observe that most of the expression
##   is null, some cells express the gene.
##
## . We need to investigate a bit deeper !

Okay, let’s get some information about these expression distributions.

# general_count_table

## Store counts in a variable
counts <- base::as.data.frame(
  # The matrix to reformat into a dataframe
  x = SeuratObject::GetAssayData(
    object = sobj, 
    assay = "RNA", 
    layer = "data")
)

## Rename cells with cell harmony cluster
base::colnames(counts) <- paste(
  # The names of the cell cluster for each cell
  sobj$CC_Seurat_Phase,
  # The names of the cells themselves
  colnames(sobj),
  sep = "_"
)

Let’s check our counts variable :

We have 2838 cells within the G1 group :

# Plac8_summaries_G1

countsG1 <- select(counts, matches("^G1."))

Plac8G1 <- base::as.data.frame(base::t(countsG1["Plac8", ]))

base::summary(Plac8G1)
Show output
     Plac8        
 Min.   :0.00000  
 1st Qu.:0.00000  
 Median :0.00000  
 Mean   :0.04823  
 3rd Qu.:0.00000  
 Max.   :3.48145  

We have 711 cells withing the S group :

# Plac8_summaries_S

countsS <- select(counts, matches("^S."))

Plac8S <- base::as.data.frame(base::t(countsS["Plac8", ]))

base::summary(Plac8S)
Show output
     Plac8       
 Min.   :0.0000  
 1st Qu.:0.0000  
 Median :0.0000  
 Mean   :0.2097  
 3rd Qu.:0.0000  
 Max.   :3.5292  

4.2.2 From biology to statistics

Okay, let us resort on statistics to evaluate our chances to be guess correctly, or our risks to guess wrong.

We have lots of observations :

  • 2838 cells within the G1 phase
  • 711 cells within the S phase

Statisticians really like to have a lot of observations !

Ideally, statisticians never have enough observations, but at least they prefer when they have more than tests.

Here, we have a total of 3549 observations and we are testing a single gene.

We live in a statistician’s wet dream !

Now, if we think like a statistician, we can ask ourselves :

  • Are our cells supposed to be interacting with each others ?
  • Or are they independent from each others ?
  • Does the expression in our cells follow a normal distribution ?

All of this is very important, and usually, it requires a discussion.

For the normal distribution, there is an easy check. Let’s draw the expression like we did above.

First, we use the function rbind from base package. Be careful, the function rbind also exists in DelayedArray, data.table, and BiocGenerics packages ! We want to use the “basic” one.

# distribution_Plac8_table_build

## Add column idientifiers in each count dataframes
Plac8G1$phase <- "G1"
Plac8S$phase <- "S"
## Paste the rows beneith each other
Plac8 <- base::rbind(
  ##  variable pointing to G1 counts
  Plac8G1,
  ##  variable pointing to S counts
  Plac8S,
  ## A Boolean, requesting that strings/characters
  ## should not be casted as `logical`. It breaks graphs.
  stringsAsFactors = FALSE

  )

Secondly, we use the function gghistogram from the package ggpubr in order to display relative abundance of gene expression :

# distribution_Plac8_display

ggpubr::gghistogram(
  Plac8,
  x = "Plac8",
  y = '..density..',
  fill = "steelblue",
  bins = 15,
  add_density = TRUE
)
Show plot

Our distribution doesn’t seem to be normal, nor binomial … we will have to rely on non-parametric tests.

Let’s run a non-parametric test based on the mean of distributions, since it’s the clothest to our ‘intuitive’ approach. Let there be Wilcoxon test.

In R, it’s quite straightforward : we have the function wilcoxon_test to perform the test, then we can plot the result.

# wilcoxon_Plac8

## On the expression table stored in the varialbe `Plac8`,
## first apply the function `wilcox_test` from package `rstatix`,
## then we apply the function `add_significance` from package `rstatix`
stat.test <- Plac8 %>% rstatix::wilcox_test(Plac8 ~ phase) %>% rstatix::add_significance()

# While doing so, we usually also compute effect size
eff.size <- Plac8 %>% rstatix::wilcox_effsize(Plac8 ~ phase)

Wilcoxon test says: the distributions are different, with a 3^{-13} % of risks of being wrong. The gene Plac8 can safely be said differentially expressed.

We can compute a fold change and conclude.

NOTE : Just out of curiosity, be aware that t_test from rstatix package, gives the same answer (with different significance). However, DESeq gives a p-value of 0.05 and an adjusted p-value equal to 1 !


Depending on the test used, this gene is, or is not differentially expressed.

4.3 Conclusion

In conclusion, to select your DEA method, use the following rules of thumb :

  1. If you have already done a study with one of these methods, keep using the same. This is crutial if you ever want to compare your new result with the old ones.
  2. If you want to compare your study with a published one, use the same method.
  3. If you have no idea, use Wilcoxon.
  4. If you have bulk data analyzed with DESeq2/Limma, use DESeq2/Limma. It will be easier to take both works in consideration.

Please, never ever use a simple Wilcoxon on bulk RNA-Seq data !!

4.4 NB MAST DE

MAST uses a hurdle model divided into two parts:

  • Discrete – models the probability that a feature is detected in a cell (present or not).
  • Continuous – models the level of expression for the feature in cells where it is detected.

MAST also uses the Cellular Detection Rate (CDR) as a covariate to adjust for sequencing effects, calculated as the fraction of detected genes per cell (nFeature barcode >0 / nHVG). It is also possible to include additional covariates, such as batch or experimental metrics, when analyzing more than two samples.

5 Select a dataset

5.1 Dataset depends on selected method

There it is quite easier:

5.2 FindMarkers

With the function FindMarkers from package Seurat, we want to make three groups:

  1. One using wilcoxon to perform DEA between clusters “8” and “10”.
  2. One using t-test to perform DEA between clusters “8” and “10”.
  3. One using MAST to perform DEA between “8” and “10”.

We will observe the results and compare our gene lists.

Hey, why are you looking at me? It’s your turn to work! Use the all the notions seen above to select the right counts (slot), the right input object, and the right arguments.

10 minutes practice !

Answers

Here are the code for each team:

sobj_wilcox <- Seurat::FindMarkers(
  # The variable that contains Seurat Object
  object = sobj,
  # Name of condition 1
  ident.1 = "8",
  # Name of condition 2
  ident.2 = "10",
  # Factor name in the Seurat Object
  group.by = "HarmonyStandalone_clusters",
  # Differential analysis method
  test.use = "wilcox"
)

sobj_t <- Seurat::FindMarkers(
  # The variable that contains Seurat Object
  object = sobj,
  # Name of condition 1
  ident.1 = "8",
  # Name of condition 2
  ident.2 = "10",
  # Factor name in the Seurat Object
  group.by = "HarmonyStandalone_clusters",
  # Differential analysis method
  test.use = "t"
)

sobj_mast <- Seurat::FindMarkers(
  # The variable that contains Seurat Object
  object = sobj,
  # Name of condition 1
  ident.1 = "8",
  # Name of condition 2
  ident.2 = "10",
  # Factor name in the Seurat Object
  group.by = "HarmonyStandalone_clusters",
  # Differential analysis method
  test.use = "MAST"
)


In the function argument, there is a FoldChange threshold. Should we filter gene expression based on FoldChange? In case of positive answer, how much should that threshold be?

Answer

About thresholds on FDR (False Discovery Rate) and Log2(FC) (Log of the Fold Change), there are many discussions.

Remember, here the threshold on Fold Change is Logged. A log2(1) =0. And keep in mind the following:

  1. If one selects a fold change threshold above 1.7, then their study concludes that smoking is not related to lung cancer.
  2. If one selects a fold change threshold above 1, then their study concludes that a fast-food based diet does not lead to weight gain.
  3. If one selects a fold change threshold above 1/25 000 000, then their study concludes: it is a complete hazard that mice have fetal malformation when in presence of Bisphanol.

In conclusion, there one, and only one reason to filter on fold change: in my experience, a fold change below 0.7 will be hard to see/verify on wet-lab (qRT).

If you need to reduce a too large number of differentially expressed genes, then reduce the FDR to 0.01, or even better, to 0.001. With that, you reduce your number of false claims.


Can you help me with DEseq2?

When I run the following command line, I have an error :

sobj_deseq2 <- Seurat::FindMarkers(
  # The variable that contains Seurat Object
  object = sobj,
  # Name of condition 1
  ident.1 = 8,
  # Name of condition 2
  ident.2 = 10,
  # Factor name in the Seurat Object
  group.by = "HarmonyStandalone_clusters",
  # Differential analysis method
  test.use = "deseq2",
  # Use non-normalized data with DESeq2
  slot = "counts"
)

Error in PerformDE(object = object, cells.1 = cells.1, cells.2 = cells.2, : Unknown test: deseq2

Answer

Oh! My fault, it was a typo in my command! Thank you all for your help!

sobj_deseq2 <- Seurat::FindMarkers(
  # The variable that contains Seurat Object
  object = sobj,
  # Name of condition 1
  ident.1 = "8",
  # Name of condition 2
  ident.2 = "10",
  # Factor name in the Seurat Object
  group.by = "HarmonyStandalone_clusters",
  # Differential analysis method
  test.use = "DESeq2",
  # Use non-normalized data with DESeq2
  slot = "counts"
)

Remark: by doing surch modification, some fold changes have been modified: remember the gene Atad2 with a mean expression of 0.08 in G1, and 0.2 in S phases? Mean expressions are now around 1.08 for G1, and 1.2 for S phases. This may be the reason why it was not differentially expressed in DESeq2, while Wilcoxon and T-test returned adjusted pvalues far below 0.05.

6 Explore results

6.1 Big tables

Let us have a look at the results:

We have the following columns:

  1. p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.
  2. avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expressed between samples in each condition.
  3. pct.1: Percent of cells with gene expression in condition one, here in cluster 8.
  4. pct.2: Percent of cells with gene expression in condition two, here in cluster 10.
  5. p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error.

We have the following columns:

  1. p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.
  2. avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expressed between samples in each condition.
  3. pct.1: Percent of cells with gene expression in condition one, here in cluster 8.
  4. pct.2: Percent of cells with gene expression in condition two, here in cluster 10.
  5. p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error.

We have the following columns:

  1. p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.
  2. avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expressed between samples in each condition.
  3. pct.1: Percent of cells with gene expression in condition one, here in cluster 8.
  4. pct.2: Percent of cells with gene expression in condition two, here in cluster 10.
  5. p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error.

We have the following columns:

  1. p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.
  2. avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expessed between samples in each condition.
  3. pct.1: Percent of cells with gene expression in condition one, here in cluster 8.
  4. pct.2: Percent of cells with gene expression in condition two, here in cluster 10.
  5. p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error.

6.2 Filter DEA results

What kind of threshold should be used to filter each results?

If we must label certain scores as good or bad, we can reference the following rule of thumb:

0.5 = No discrimination 0.5-0.7 = Poor discrimination 0.7-0.8 = Acceptable discrimination 0.8-0.9= Excellent discrimination 0.9 = Outstanding discrimination

Hosmer and Lemeshow in Applied Logistic Regression (p. 177)

6.3 Add results to Seurat objects

We’d like to store the results of differential expression analysis in the Seurat object.

sobj@misc$wilcox <- sobj_wilcox

6.4 Common results

Now we can plot intersections in an up-set graph. It is like a Venn diagram:

UpSetR::upset(
  data = UpSetR::fromList(data),
  order.by = "freq"
)
Show plot

6.5 Others functions in Seurat for DEA

There are two other functions in Seurat package to do DEA in different context. For example, you will see the difference between one cluster versus many of them. You could be use FindAllMarkers. Usage is equivalent to FindMarkers but it need to indicate which variable cluster variable.

sobj_findallmarkers_mast <- FindAllMarkers(sobj,
                                           assay="RNA",
                                           group.by="HarmonyStandalone_clusters",
                                           test.use = "MAST",
                                           only.pos = T,
                                           logfc.threshold = 0.9,
                                           min.pct = 0.1,
                                           random.seed = my_seed)

DT::datatable(
  head(sobj_findallmarkers_mast, n = 10),
  caption = "MAST results"
)
p <- EnhancedVolcano(
  toptable = sobj_findallmarkers_mast,
  lab = rownames(sobj_findallmarkers_mast),
  x = "avg_log2FC",
  y = "p_val_adj",
  FCcutoff = 0.2,
  drawConnectors = FALSE,
  title = 'Volcano by cluster'
)

p + facet_wrap(~ cluster)
Show plot

Another function is FindConservedMarkers. It the same function of FindMarkers but this usage is to identify the same features expression between clusters

sobj_conservedmarkers_mast <- Seurat::FindConservedMarkers(
  # The variable that contains Seurat Object
  object = sobj,
  # Name of condition 1
  ident.1 = "8",
  # Name of condition 2
  ident.2 = "10",
  # Factor name in the Seurat Object
  grouping.var = "orig.ident",
  # Differential analysis method
  assay="RNA",
  slot="counts"
)

DT::datatable(
  head(sobj_conservedmarkers_mast, n = 10),
  caption = "MAST results"
)
  1. TDCT_p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.

  2. TDCT_avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expessed between samples in TDCT condition.

  3. TDCT_pct.1: Percent of cells with gene expression in condition one, here in cluster 8 in TDCT condition.

  4. TDCT_pct.2: Percent of cells with gene expression in condition two, here in cluster 10 in TDCT condition.

  5. TDCT_p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error in TDCT condition.

  6. TD3A_p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.

  7. TD3A_avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expessed between samples in TD3A condition.

  8. TD3A_pct.1: Percent of cells with gene expression in condition one, here in cluster 8 in TD3A condition.

  9. TD3A_pct.2: Percent of cells with gene expression in condition two, here in cluster 10 in TD3A condition.

  10. TD3A_p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error in TD3A condition.

  11. max_pval: Maximum of p_val between comparison TDCT vs TD3A. It must be low to indicate a conserved feature

  12. minimump_p_val: Minimum of p_val between comparison TDCT vs TD3A.

Seurat::VlnPlot(
  # A subset of the Seurat object
  # limited to clusters 8 and 10, 
  # or else we will plot all the clusters
  object = subset(sobj, HarmonyStandalone_clusters %in% c("8", "10")),
  
  # The name of the gene of interest (feature = gene)
  features = "Plac8",
  
  # The name of the Seurat cell annotation
  split.by = "orig.ident",
  
  # Change color for presentation
  cols = c("blue", "red")
  
)
Show plot

sobj_conservedmarkers_mast$direction = case_when(
  sign(sobj_conservedmarkers_mast$TDCT_avg_log2FC) == sign(sobj_conservedmarkers_mast$TD3A_avg_log2FC) &
    sign(sobj_conservedmarkers_mast$TDCT_avg_log2FC) == -1 ~ "negative",
  sign(sobj_conservedmarkers_mast$TDCT_avg_log2FC) == sign(sobj_conservedmarkers_mast$TD3A_avg_log2FC) &
    sign(sobj_conservedmarkers_mast$TDCT_avg_log2FC) == 1 ~ "positive",
  .default = "opposite")


t_dataframe <- table(sobj_conservedmarkers_mast$direction,sobj_conservedmarkers_mast$max_pval<0.05)[,2]

levels_direction <- paste0(names(t_dataframe)," (nFeature=",t_dataframe,")")

sobj_conservedmarkers_mast_filtered = sobj_conservedmarkers_mast[sobj_conservedmarkers_mast$minimump_p_val<0.05,]
sobj_conservedmarkers_mast_filtered$direction = factor(sobj_conservedmarkers_mast_filtered$direction,
                                                              levels=c("negative","opposite","positive"),
                                                              labels=levels_direction)  

ggplot() +
  geom_point(data=sobj_conservedmarkers_mast_filtered,
             aes(x = TDCT_avg_log2FC, y = TD3A_avg_log2FC, color = direction)) +
  scale_color_manual(values=c("lightblue","grey70","red")) +

  theme_bw() +
  labs(x = "TDCT Average Log2 FoldChange cluster 8 vs cluster 12",
       y = "TD3A Average Log2 FoldChange cluster 8 vs cluster 12",
       title = "Result of Wilcoxon Test between cluster 8 vs cluster 12 between condition TDCT vs TD3A",
       color = "Direction Log2FoldChange") 
Show plot

6.6 Heatmap

We’d like to display the expression of genes identified by FindMarkers. Then we use the function DoHeatmap from the package Seurat.

In order to limit the graph to differentially expressed reads, we use the function rownames from R base package on the DEA result table. In this example, I use the results of wilcoxon, but you shall use any of the results you previously obtained.

Seurat::DoHeatmap(
  # variable pointing to seurat object
  object = sobj,
  # name of DE genes
  features = base::rownames(sobj_wilcox),
  # Cluster annotation
  group.by = "HarmonyStandalone_clusters",
)
Show plot

6.7 Volcano plot

A Volcano plot is usefull to identify differnetial expression analysis bias.

The package EnhancedVolcano has an eponym function for that:

EnhancedVolcano::EnhancedVolcano(
  #  variable pointing to the DEA results
  toptable = sobj_wilcox,
  # Gene names
  lab = rownames(sobj_wilcox),
  # Column in which to find Fold Change
  x = "avg_log2FC",
  # Column in which to find confidence interval
  y = "p_val_adj",
  # Lower fold-change cut-off
  FCcutoff = 0.2
)
Show plot

6.8 Session Info

This list of all packages used while you work should be included in each en every R presentation:

utils::sessionInfo()
Show output
R version 4.3.1 (2023-06-16)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: Ubuntu 20.04.6 LTS

Matrix products: default
BLAS:   /home/mna_bioinfo/anaconda3/envs/r431/lib/libblas.so.3.9.0 
LAPACK: /home/mna_bioinfo/anaconda3/envs/r431/lib/liblapack.so.3.9.0

locale:
 [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=fr_FR.UTF-8    
 [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
 [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Paris
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] EnhancedVolcano_1.20.0      ggrepel_0.9.6              
 [3] UpSetR_1.4.0                SingleCellExperiment_1.24.0
 [5] SummarizedExperiment_1.32.0 Biobase_2.62.0             
 [7] GenomicRanges_1.54.1        GenomeInfoDb_1.38.8        
 [9] IRanges_2.36.0              S4Vectors_0.40.2           
[11] BiocGenerics_0.48.1         MatrixGenerics_1.14.0      
[13] matrixStats_1.5.0           Seurat_5.3.1               
[15] SeuratObject_5.2.0          sp_2.2-0                   
[17] dplyr_1.1.4                 knitr_1.50                 
[19] rstatix_0.7.3               ggpubr_0.6.2               
[21] ggplot2_3.5.2               DT_0.34.0                  
[23] BiocParallel_1.36.0        

loaded via a namespace (and not attached):
  [1] RcppAnnoy_0.0.22        splines_4.3.1           later_1.4.4            
  [4] bitops_1.0-9            tibble_3.3.0            polyclip_1.10-7        
  [7] fastDummies_1.7.5       lifecycle_1.0.4         Rdpack_2.6.4           
 [10] globals_0.18.0          lattice_0.22-7          MASS_7.3-60.0.1        
 [13] MAST_1.28.0             crosstalk_1.2.2         backports_1.5.0        
 [16] magrittr_2.0.4          limma_3.58.1            plotly_4.11.0          
 [19] sass_0.4.10             rmarkdown_2.30          plotrix_3.8-4          
 [22] jquerylib_0.1.4         yaml_2.3.10             qqconf_1.3.2           
 [25] httpuv_1.6.16           otel_0.2.0              sn_2.1.1               
 [28] sctransform_0.4.2       spam_2.11-1             spatstat.sparse_3.1-0  
 [31] reticulate_1.44.0       cowplot_1.2.0           pbapply_1.7-4          
 [34] RColorBrewer_1.1-3      multcomp_1.4-29         abind_1.4-8            
 [37] zlibbioc_1.48.2         Rtsne_0.17              presto_1.0.0           
 [40] purrr_1.1.0             RCurl_1.98-1.17         TH.data_1.1-4          
 [43] sandwich_3.1-1          GenomeInfoDbData_1.2.11 irlba_2.3.5.1          
 [46] listenv_0.9.1           spatstat.utils_3.2-0    TFisher_0.2.0          
 [49] goftest_1.2-3           RSpectra_0.16-2         spatstat.random_3.4-2  
 [52] fitdistrplus_1.2-4      parallelly_1.45.1       coin_1.4-3             
 [55] codetools_0.2-20        DelayedArray_0.28.0     tidyselect_1.2.1       
 [58] farver_2.1.2            rmdformats_1.0.4        spatstat.explore_3.5-3 
 [61] mathjaxr_1.8-0          jsonlite_2.0.0          multtest_2.58.0        
 [64] progressr_0.17.0        Formula_1.2-5           ggridges_0.5.7         
 [67] survival_3.8-3          progress_1.2.3          tools_4.3.1            
 [70] ica_1.0-3               Rcpp_1.1.0              glue_1.8.0             
 [73] mnormt_2.1.1            gridExtra_2.3           SparseArray_1.2.4      
 [76] metap_1.12              DESeq2_1.42.1           xfun_0.54              
 [79] numDeriv_2016.8-1.1     withr_3.0.2             fastmap_1.2.0          
 [82] digest_0.6.37           R6_2.6.1                mime_0.13              
 [85] colorspace_2.1-2        scattermore_1.2         tensor_1.5.1           
 [88] dichromat_2.0-0.1       spatstat.data_3.1-9     tidyr_1.3.1            
 [91] generics_0.1.4          data.table_1.17.8       prettyunits_1.2.0      
 [94] httr_1.4.7              htmlwidgets_1.6.4       S4Arrays_1.2.1         
 [97] uwot_0.2.3              pkgconfig_2.0.3         gtable_0.3.6           
[100] modeltools_0.2-24       lmtest_0.9-40           XVector_0.42.0         
[103] htmltools_0.5.8.1       carData_3.0-5           dotCall64_1.2          
[106] bookdown_0.45           scales_1.4.0            png_0.1-8              
[109] spatstat.univar_3.1-4   rstudioapi_0.17.1       reshape2_1.4.4         
[112] nlme_3.1-168            cachem_1.1.0            zoo_1.8-14             
[115] stringr_1.5.2           KernSmooth_2.23-26      libcoin_1.0-10         
[118] vipor_0.4.7             parallel_4.3.1          miniUI_0.1.2           
[121] ggrastr_1.0.2           pillar_1.11.1           grid_4.3.1             
[124] vctrs_0.6.5             RANN_2.6.2              promises_1.4.0         
[127] car_3.1-3               xtable_1.8-4            cluster_2.1.8.1        
[130] beeswarm_0.4.0          evaluate_1.0.5          locfit_1.5-9.12        
[133] mvtnorm_1.3-3           cli_3.6.5               compiler_4.3.1         
[136] rlang_1.1.6             crayon_1.5.3            mutoss_0.1-13          
[139] future.apply_1.20.0     ggsignif_0.6.4          labeling_0.4.3         
[142] ggbeeswarm_0.7.2        plyr_1.8.9              stringi_1.8.7          
[145] viridisLite_0.4.2       deldir_2.0-4            lazyeval_0.2.2         
[148] spatstat.geom_3.6-0     Matrix_1.6-5            RcppHNSW_0.6.0         
[151] hms_1.1.4               patchwork_1.3.2         future_1.67.0          
[154] statmod_1.5.1           shiny_1.11.1            rbibutils_2.3          
[157] ROCR_1.0-11             igraph_2.2.1            broom_1.0.10           
[160] bslib_0.9.0            
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj48Qj5EaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpczwvQj48L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUJBSUkgbjEgc2NSTkFzZXEgVGVhbSIKICAtIG5hbWU6ICJUaGliYXVsdCBEQVlSSVMiCiAgICBlbWFpbDogInRoaWJhdWx0LmRheXJpc0BndXN0YXZlcm91c3N5LmZyIgogIC0gbmFtZTogIkJhc3RpZW4gSk9CIgogICAgZW1haWw6ICJiYXN0aWVuLmpvYkBndXN0YXZlcm91c3N5LmZyIgogIC0gbmFtZTogIldpbGxpYW0gSkFSQVNTSUVSIgogICAgZW1haWw6ICJ3LmphcmFzc2llckBnbWFpbC5jb20iCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNgogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgZW1iZWRfZm9udHM6IFRSVUUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgIHRvY19kZXB0aDogMwogICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCgo8IS0tIGtuaXQgc2V0dXAgLS0+CgpgYGB7ciBrbml0X3NldHVwLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgICAgICAgICMgUHJpbnQgdGhlIGNvZGUKICBldmFsID0gVFJVRSwgICAgICAgICMgUnVuIGNvbW1hbmQgbGluZXMKICBtZXNzYWdlID0gRkFMU0UsICAgICMgUHJpbnQgbWVzc2FnZXMKICBwcm9tcHQgPSBGQUxTRSwgICAgICMgRG8gbm90IGRpc3BsYXkgcHJvbXB0CiAgY29tbWVudCA9IE5BLCAgICAgICAjIE5vIGNvbW1lbnRzIG9uIHRoaXMgc2VjdGlvbgogIHdhcm5pbmcgPSBGQUxTRSwgICAgIyBEaXNwbGF5IHdhcm5pbmdzCiAgdGlkeSA9IEZBTFNFLAogIGZpZy5hbGlnbj0iY2VudGVyIiwgCiAgIyByZXN1bHRzID0gJ2hpZGUnLAogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCmBgYHtjc3MsIGVjaG89RkFMU0V9Ci5ub3RydW4gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJsYWNrICFpbXBvcnRhbnQ7Cn0KLm5vdHJ1bm8gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQoucXVlc3Rpb24gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgbGltZWdyZWVuICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9ubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYW5zd2VyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJyb3duICFpbXBvcnRhbnQ7Cn0KLmFuc3dlcm8gewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5iZXlvbmQgewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIHB1cnBsZSAhaW1wb3J0YW50Owp9Ci5iZXlvbmRvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KYGBgCgo8IS0tIEhvb2sgdG8gaGFuZGxlIGNvZGUgYmxvY2tzIG91dHB1dCBmb2xkaW5nIC0tPgoKYGBge3Iga25pdF9ob29rLCBlY2hvID0gRkFMU0V9Cmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkLiIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT5TaG93ICIsIHR5cGUsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKQopCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxjZW50ZXI+IVtdKGltYWdlcy9lYjNpX2Jhbm5lci5wbmcpPC9jZW50ZXI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0YXJ0IFJzdHVkaW8KCi0gICBVc2luZyB0aGUgW09wZW5PbkRlbWFuZC9Sc3R1ZGlvIGNoZWF0CiAgICBzaGVldF0oaHR0cHM6Ly9tb29kbGUuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mci9wbHVnaW5maWxlLnBocC8xNDc1L21vZF9mb2xkZXIvY29udGVudC8wL09vRF9SX1JzdHVkaW8uaHRtbCksCiAgICBjb25uZWN0IHRvIHRoZSBbT3Blbk9uRGVtYW5kCiAgICBwb3J0YWxdKGh0dHBzOi8vb25kZW1hbmQuY2x1c3Rlci5mcmFuY2UtYmlvaW5mb3JtYXRpcXVlLmZyKSBhbmQKICAgIGNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbiB3aXRoIHRoZSByaWdodCByZXNvdXJjZSByZXF1aXJlbWVudHMuCgojIFdhcm0tdXAKCi0gICBXZSBzZXQgY29tbW9uIHBhcmFtZXRlcnMgd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGlzIHNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW0sIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUHJlcGFyZSB0aGUgZGF0YSBzdHJ1Y3R1cmUKCldlIHdpbGwgZG8gdGhlIHNhbWUgYXMgZm9yIGZvcm1lciBzdGVwcywganVzdCBjaGFuZ2luZyB0aGUgc2Vzc2lvbiBuYW1lCjoKCiMjIE1haW4gZGlyZWN0b3J5CgpgYGB7ciBtYWluZGlyLGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiNtYWluZGlyCgojIyBQcmVwYXJpbmcgdGhlIHBhdGgKVERfZGlyIDwtIHBhc3RlMCgiL3NoYXJlZC9wcm9qZWN0cy8iLCBwcm9qZWN0X25hbWUsICIvU0NfVEQiKQoKIyMgQ3JlYXRpbmcgdGhlIHJvb3QgZGlyZWN0b3J5CiMgZGlyLmNyZWF0ZShwYXRoID0gVERfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJvb3QgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChURF9kaXIpCgpgYGAKCiMjIEN1cnJlbnQgc2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmRpcixldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIHNlc3Npb25kaXIKCiMjIENyZWF0aW5nIHRoZSBzZXNzaW9uIChQcmVwcm9jLjIpIGRpcmVjdG9yeQpzZXNzaW9uX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiLzA1X1Byb2MuMiIpCmRpci5jcmVhdGUocGF0aCA9IHNlc3Npb25fZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHNlc3Npb24gZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChzZXNzaW9uX2RpcikKCmBgYAoKIyMgSW5wdXQgZGlyZWN0b3J5CgpgYGB7ciBpbmRpciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KI2luZGlyCgojIyBDcmVhdGluZyB0aGUgSU5QVVQgZGF0YSBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9EQVRBIikKZGlyLmNyZWF0ZShwYXRoID0gaW5wdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIGlucHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoaW5wdXRfZGlyKQoKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXIsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiNvdXRkaXIKCiMjIENyZWF0aW5nIHRoZSBPVVRQVVQgZGF0YSBkaXJlY3RvcnkKb3V0cHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvUkVTVUxUUyIpCmRpci5jcmVhdGUocGF0aCA9IG91dHB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgb3V0cHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQob3V0cHV0X2RpcikKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWQsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMjICBkYXRhbG9hZAoKCiMjIFRoaXMgaXMgdGhlIHBhdGggdG8gdGhlIGN1cnJlbnQgRUIzSSBiYWNrdXAKc2Vzc2lvbmlkIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKCgojIyBUaGUgbGF0ZXN0IFNldXJhdCBvYmplY3Qgc2F2ZWQgYXMgUkRTIChuYW1lKQoKc29ial9maWxlIDwtICIxMl9URDNBLlREQ1RfUzVfSW50ZWdyYXRlZF8xMjkyNi4zODg2LlJEUyIKCiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKGZ1bGwgcGF0aCkKc29ial9wYXRoIDwtIHBhc3RlMChURF9kaXIsIAogICAgICAgICAgICAgICAgICAgICIvMDVfREVBL1JFU1VMVFMvIiwKICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCgpmb3JjZSA8LSBUUlVFICAjIyBUbyBmb3JjZSBhIHJlLWRvd25sb2FkIG9mIGEgWmVub2RvLWhvc3RlZCBiYWNrdXAKbG9jYWwgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgbG9hZGluZyBmcm9tIGEgbG9jYWwgYmFja3VwCgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBaZW5vZG8gYmFja3VwIHJlcG9zaXRvcnkKaWYoZm9yY2UpIHsKICB6ZW5faWQgPC0gIjE0MDM1MjkzIgogIHplbl9iYWNrdXBfZmlsZSA8LSBwYXN0ZTAoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL2ZpbGVzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCiAgIyMgUmVjcmVhdGUgdGhlIGV4cGVjdGVkIHBhdGggaWYgaXQgZG9lcyBub3QgZXhpc3QKICBkaXIuY3JlYXRlKHBhdGggPSBkaXJuYW1lKHNvYmpfcGF0aCksIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc29ial9wYXRoKQp9CgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBsb2NhbCBiYWNrdXAgcmVwb3NpdG9yeQppZihsb2NhbCkgewogIHNvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgICAiL3NoYXJlZC9wcm9qZWN0cy8iLCBzZXNzaW9uaWQsICIvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvUkRTLyIsCiAgICBzb2JqX2ZpbGUpCn0KCiMjIExvYWQgdGhlIG9iamVjdApzb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHNvYmpfcGF0aCkKCmBgYAoKYGBge3Igbm90X3Nob3dfbG9hZGluZ19kYXRhLCBpbmNsdWRlPUZBTFNFfQoKc29iaiA8LSByZWFkUkRTKCIwNV9ERUEvUkVTVUxUUy8xMl9URDNBLlREQ1RfUzVfSW50ZWdyYXRlZF8xMjkyNi4zODg2LlJEUyIpCnNvYmogPC0gU2V1cmF0T2JqZWN0OjpKb2luTGF5ZXJzKHNvYmopCmBgYAoKCiMgRm9yZXdvcmRzCgpJIG5lZWQgdGhyZWUgdGVhbXMgZm9yIHRoaXMgc2Vzc2lvbjogCi0gICBUZWFtIFtgV2lsY294b25gXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XaWxjb3hvbl9zaWduZWQtcmFua190ZXN0KQotICAgVGVhbSBbYFN0dWRlbnRgXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdHVkZW50JTI3c190LXRlc3QpIAotICAgVGVhbSBbYE1BU1RgXQoKSSB3aWxsIGFsc28gbmVlZCB5b3VyIGhlbHAgYmVjYXVzZSBJIGNhbid0IG1ha2UgW0RFU2VxMl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0RFU2VxMi5odG1sKSAKd29yayBjb3JyZWN0bHkuIAoKQnV0IEknbSBzdXJlLCB0aGF0IHdlIHdpbGwgc29sdmUgbXkgaXNzdWU6IHlvdSdyZSBpbiB0aGUgYmVzdCAKc2Vzc2lvbiBoZXJlIGF0IFtFQjNJXShodHRwczovL2dpdGh1Yi5jb20vSUZCLUVsaXhpckZyL0VCQUlJKS4KCiMjIFRMRFI6IFIgY29tbWFuZCBsaW5lcwoKSW4gdGhpcyBwcmVzZW50YXRpb24sIHRoZXJlIHdpbGwgYmUgc2NyZWVuIGNhcHR1cmVzIGZvciB5b3UgdG8gZm9sbG93IHRoZSAKbGVzc29uLiBUaGVyZSB3aWxsIGFsc28gYmUgZXZlcnkgc2luZ2xlIFIgY29tbWFuZCBsaW5lcy4gCgpEbyBub3QgdGFrZSBjYXJlIG9mIHRoZSBjb21tYW5kIGxpbmVzIGlmIHlvdSBmaW5kIHRoZW0gdG9vIGNoYWxsZW5naW5nLiAKCk91ciBnb2FsIGhlcmUsIGlzIHRvICoqdW5kZXJzdGFuZCoqIHRoZSBtYWluIG1lY2hhbmlzbSBvZiBEaWZmZXJlbnRpYWwgCkV4cHJlc3Npb24gQW5hbHlzaXMuIFIgaXMgX2p1c3QgYSB0b29sXy4KCkJlbG93IGFyZSB0aGUgbGlicmFyaWVzIHdlIG5lZWQgdG8gcGVyZm9ybSB0aGlzIHdob2xlIHNlc3Npb246CgpgYGB7ciBsb2FkX2xpYnJhcmllcywgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KYmFzZTo6bGlicmFyeShwYWNrYWdlID0gIkJpb2NQYXJhbGxlbCIpICAgICMgT3B0aW9uYWxseSBtdWx0aXRocmVhZCBzb21lIHN0ZXBzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJEVCIpICAgICAgICAgICAgICAjIERpc3BsYXkgbmljZSB0YWJsZXMgaW4gSFRNTApiYXNlOjpsaWJyYXJ5KHBhY2thZ2UgPSAiZ2dwbG90MiIpICAgICAgICAgIyBEcmF3IG5pY2UgZ3JhcGhzIGFuZCBwbG90cwpiYXNlOjpsaWJyYXJ5KHBhY2thZ2UgPSAiZ2dwdWJyIikgICAgICAgICAgIyBEcmF3IGV2ZW4gbmljZXIgZ3JhcGhzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJyc3RhdGl4IikgICAgICAgICAjIEJhc2UgUiBzdGF0aXN0aWNzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJrbml0ciIpICAgICAgICAgICAjIEJ1aWxkIHRoaXMgcHJlc2VudGF0aW9uCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJkcGx5ciIpICAgICAgICAgICAjIEhhbmRsZSBiaWcgdGFibGVzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJTZXVyYXQiKSAgICAgICAgICAjIEhhbmRsZSBTaW5nbGVDZWxsIGFuYWx5c2VzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJTZXVyYXRPYmplY3QiKSAgICAjIEhhbmRsZSBTaW5nbGVDZWxsIG9iamVjdHMgZm9yIFNldXJhdApiYXNlOjpsaWJyYXJ5KHBhY2thZ2UgPSAiU2luZ2xlQ2VsbEV4cGVyaW1lbnQiKSAjIEhhbmRsZSBTaW5nbGVDZWxsIGZpbGUgZm9ybWF0cwpiYXNlOjpsaWJyYXJ5KHBhY2thZ2UgPSAiVXBTZXRSIikgICAgICAgICAgIyBOaWNlIHZlbm4tbGlrZSBncmFwaHMKYmFzZTo6bGlicmFyeShwYWNrYWdlID0gIkVuaGFuY2VkVm9sY2FubyIpICMgRHJhdyBWb2xjYW5vIHBsb3QKYGBgCgpUaGVuIHdlIGpvaW4gbGF5ZXJzOgoKYGBge3Igam9pbl9sYXllcnNfdGxkciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMgam9pbl9sYXllcnNfdGxkcgoKU2V1cmF0OjpJZGVudHMoc29iaikgPC0gc29iaiRvcmlnLmlkZW50CnNvYmogPC0gU2V1cmF0T2JqZWN0OjpKb2luTGF5ZXJzKHNvYmopCgpgYGAKClRoZW4gd2UgcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpczoKCmBgYHtyIHJ1bl9kZWEsIGV2YWw9RkFMU0UsIGVjaG89VFJVRSwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIHJ1bl9kZWEKCnNvYmpfZGUgPC0gU2V1cmF0OjpGaW5kTWFya2VycygKICAjIE9iamVjdCBvbiB3aGljaCB0byBwZXJmb3JtIERFQQogIG9iamVjdCA9IHNvYmosCiAgIyBOYW1lIG9mIGZhY3RvciBpbiBjb25kaXRpb24gMQogIGlkZW50LjEgPSAiVEQzQSIsCiAgIyBOYW1lIG9mIGZhY3RvciBpbiBjb25kaXRpb24gMgogIGlkZW50LjIgPSAiVERDVCIKKQoKYGBgCgpBbmQgdGhhdCdzIGFsbCAhIE91ciBnb2FsIGlzIHRvIHVuZGVyc3RhbmQgdGhlc2UgbGluZXMsCmJlaW5nIGFibGUgdG8gd3JpdGUgdGhlbSBpcyBhIGJvbnVzLgoKCiMjIFB1cnBvc2Ugb2YgdGhpcyBzZXNzaW9uCgpVcCB0byBub3csIHdlIGhhdmU6CgoxLiBJZGVudGlmaWVkIHRvIHdoaWNoIGNlbGwgZWFjaCBzZXF1ZW5jZWQgcmVhZHMgY29tZSBmcm9tCjIuIElkZW50aWZpZWQgdG8gd2hpY2ggZ2VuZSBlYWNoIHJlYWQgY29tZSBmcm9tCjMuIElkZW50aWZpZWQgcG9zc2libGUgYmlhcyBpbiBnZW5lIGV4cHJlc3Npb24gZm9yIGVhY2ggY2VsbAo0LiBGaWx0ZXJlZCBhbmQgY29ycmVjdGVkIHRoZXNlIGJpYXMgYXMgd2VsbCBhcyB3ZSBjYW4KCldlIHdvdWxkIGxpa2UgdG8gaWRlbnRpZnkgdGhlIGxpc3Qgb2YgZ2VuZXMgdGhhdCBjaGFyYWN0ZXJpemUgZGlmZmVyZW5jZXMgCmJldHdlZW4gY2VsbCBjeWNsZSBwaGFzZXMgRzEgYW5kIFMgZ3JvdXBzLgoKQXQgdGhlIGVuZCBvZiB0aGlzIHNlc3Npb24geW91IHdpbGwga25vdzoKCjEuIGhvdyB0byBzZWxlY3QgYSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgbWV0aG9kCjIuIGhvdyB0byBzZWxlY3QgdGhlIGNvcnJlY3Qgbm9ybWFsaXphdGlvbiAoaWYgYW55PykgdGhhdCBtdXN0IGJlIHByb3ZpZGVkIHRvIAogICB5b3VyIGRpZmZlcmVudGlhbCBhbmFseXNpcyBtZXRob2QKMy4gSG93IHRvIHJlYWQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cwoKIyMgSW5zaWdodAoKV2UgYXJlIHdvbmRlcmluZyB3aGF0J3MgaW4gb3VyIGRhdGFzZXQuIExldCdzIGhhdmUgYSBsb29rLCAKd2l0aCB0aGUgZnVuY3Rpb24gW2BwcmludGBdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9wcmludCkgCmZvcm0gdGhlIHBhY2thZ2UgYGJhc2VgLgoKYGBge3IgcHJpbnRfc2V1cmF0LCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBwcmludF9zZXVyYXQKCmJhc2U6OnByaW50KAogICMgT2JqZWN0IHRvIGRpc3BsYXkKICB4ID0gc29iagopCgpgYGAKCldlIGhhdmUgYHIgYmFzZTo6ZGltKFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIsIGxheWVyID0gImRhdGEuVEQzQSIpKVsxXWAgZmVhdHVyZXMgKF9ha2FfIGdlbmVzKSwgCmFjcm9zcyBgciBiYXNlOjpkaW0oU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEob2JqZWN0ID0gc29iaiwgYXNzYXkgPSAiUk5BIiwgbGF5ZXIgPSAiZGF0YS5URDNBIikpWzJdYCBzYW1wbGVzIChfYWthXyBjZWxscykgaW4gdGhlIFREM0EgY29uZGl0aW9uLiBXZSBoYXZlIGByIGJhc2U6OmRpbShTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiLCBsYXllciA9ICJkYXRhLlREQ1QiKSlbMV1gIGZlYXR1cmVzIChfYWthXyBnZW5lcyksIAphY3Jvc3MgYHIgYmFzZTo6ZGltKFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIsIGxheWVyID0gImRhdGEuVERDVCIpKVsyXWAgc2FtcGxlcyAoX2FrYV8gY2VsbHMpIGluIHRoZSBURDNBIGNvbmRpdGlvbi4KCgpMZXQgdXMgaGF2ZSBhIGxvb2sgYXQgdGhlIFJOQSBjb3VudHMgZm9yIDEwIGNlbGxzIGFuZCB0aGVpciBhbm5vdGF0aW9uLAp3aXRoIHRoZSBmdW5jdGlvbiBbYGhlYWRgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvdXRpbHMvdmVyc2lvbnMvMy42LjIvdG9waWNzL2hlYWQpIGZyb20gdGhlIHBhY2thZ2UgYHV0aWxzYC4KCmBgYHtyIGhlYWRfc2V1cmF0X2NvdW50c19waGFzZSwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQojIGhlYWRfc2V1cmF0X2NvdW50c19waGFzZQoKdXRpbHM6OmhlYWQoCiAgIyBPYmplY3QgdG8gdmlzdWFsaXplCiAgeCA9IHNvYmosCiAgIyBOdW1iZXIgb2YgbGluZXMgdG8gZGlzcGxheQogIG4gPSAxMAopCgpgYGAKClByZXR0aWVyIHdpdGggYSB0YWJsZSBpbiBIVE1MIDoKCmBgYHtyIGhlYWRfc2V1cmF0X2NvdW50c19waGFzZV9kdCwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQojIGhlYWRfc2V1cmF0X2NvdW50c19waGFzZV9kdAoKdG1wIDwtIHV0aWxzOjpoZWFkKHggPSBzb2JqLCBuID0gMTApCkRUOjpkYXRhdGFibGUoZGF0YSA9IHRtcCkKCmBgYAoKVGhlcmUgYXJlIG5vIGNvdW50cywgbm9ybWFsaXplZCBvciBub3QgISEKCldoZXJlIGFyZSB0aGV5ID8KCkluIG9yZGVyIHRvIGV4cGxvcmUgdGhlIGNvbnRlbnQgb2YgYHNvYmpgLCB1c2UgdGhlIGZ1bmN0aW9uIGBzdHJgIGZyb20gdGhlIHBhY2thZ2UgYHV0aWxzYDoKCmBgYHtyIHN0cl9zZXVyYXRfb2JqZWN0LCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CiMgc3RyX3NldXJhdF9vYmplY3QKCnV0aWxzOjpzdHIoCiAgIyBPYmplY3QgdG8gZXhwbG9yZQogIG9iamVjdCA9IHNvYmpAYXNzYXlzCikKCmBgYAoKQWx0ZXJuYXRpdmVseSwgaW4gUlN0dWRpbywgeW91IGNhbiBjbGljayBvbiB0aGUgb2JqZWN0IHBhbmUgYW5kIGV4cGxvcmUgbWFudWFsbHkgCnRoZSBjb250ZW50IG9mIHRoZSBvYmplY3QuIElmIHdlIGV4cGxvcmUgdGhlIHNsb3QgYGFzc2F5c2AsIHRoZW4gd2UgZmluZCB0aGUgY291bnRzLgoKWW91IGNhbiBhY2Nlc3MgdGhlbSB3aXRoOgoKYGBge3IgaGVhZF9zZXVyYXRfY291bnRfdGFibGUsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KIyBoZWFkX3NldXJhdF9jb3VudF90YWJsZQoKdXRpbHM6OmhlYWQoCiAgIyBPYmplY3QgdG8gdmlzdWFsaXplCiAgeCA9IFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIsIGxheWVyID0gImNvdW50cyIpLAogICMgTnVtYmVyIG9mIHJvd3MgdG8gZGlzcGxheQogIG4gPSAxMAoKICApCmBgYAoKUHJldHRpZXIgd2l0aCBhIHRhYmxlIGluIEhUTUwgOgoKYGBge3IgaGVhZF9zZXVyYXRfY291bnRfdGFibGVfZHQsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KIyBoZWFkX3NldXJhdF9jb3VudF90YWJsZV9kdAoKdG1wIDwtIHV0aWxzOjpoZWFkKAogIHggPSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiLCBsYXllciA9ICJjb3VudHMiKSwgCiAgMTAKKQpEVDo6ZGF0YXRhYmxlKAogIGRhdGEgPSBiYXNlOjphcy5kYXRhLmZyYW1lKHRtcClbLCAxOjVdCikKCmBgYAoKV2UgaGF2ZSBvbmUgZ2VuZSBwZXIgbGluZSwgb25lIGNlbGwgcGVyIGNvbHVtbiwgYW5kIFJOQSBjb3VudHMgaW4gZWFjaCByb3cuCgoKRm9yIHRoZSBzYWtlIG9mIHRoaXMgc2Vzc2lvbiwgd2Ugd29uJ3QgY29tcGFyZSB0aGUgd2hvbGUgZGF0YXNldC4gSXQgd291bGQgdGFrZQp1cCB0byAxNSBtaW51dGVzIHRvIGNvbXBsZXRlLiBEdXJpbmcgdGhlIHJlc3Qgb2YgdGhpcyBzZXNzaW9uLCB3ZSB3aWxsIGNvbXBhcmUKdGhlIExvdXZhaW4gY2x1c3RlcnMgOCBhbmQgMTAsIGZyb20gb3VyIGludGVncmF0aW9uIHdpdGggSGFybW9ueS4KCldlIG5lZWQgdG8gcmUtYW5ub3RhdGUgbGF5ZXJzIHRvIGRvIHNvLiBUaGlzIGlzIGRvbmUgaW4gdHdvIHN0ZXBzOiAKCjEuIFJlZGVmaW5lIFtgSWRlbnRzYF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL1NldXJhdC92ZXJzaW9ucy8zLjEuNC90b3BpY3MvSWRlbnRzKQppbiBvcmRlciB0byBiZSBhYmxlIHRvIGNhbGwgY2VsbHMgYnkgdGhlaXIgY2x1c3RlciBuYW1lcy4KMi4gW2BKb2luTGF5ZXJzYF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL1NldXJhdE9iamVjdC92ZXJzaW9ucy81LjAuMi90b3BpY3MvSm9pbkxheWVycykgdG8gaGF2ZSBhIHNpbmdsZSBjb3VudCB0YWJsZSB3aXRoIGJvdGggVEQzQSBhbmQgVERDVCBjZWxscyBmcm9tIGNsdXN0ZXJzIDggYW5kIDEwIHRvZ2V0aGVyLgoKYGBge3IgaWRlbnRfYW5kX2pvaW5fbGF5ZXJzLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBpZGVudF9hbmRfam9pbl9sYXllcnMKClNldXJhdDo6SWRlbnRzKHNvYmopIDwtIHNvYmokSGFybW9ueVN0YW5kYWxvbmVfY2x1c3RlcnMKc29iaiA8LSBTZXVyYXRPYmplY3Q6OkpvaW5MYXllcnMoc29iaikKCmBgYAoKTGV0J3MgY2hlY2sgaWYgb3VyIGNlbGxzIGFyZSBqb2ludDoKCmBgYHtyIGNoZWNrX2pvaW50X2xheWVyc30KIyBjaGVja19qb2ludF9sYXllcnMKCmJhc2U6OnByaW50KHNvYmopCgpgYGAKCgoqKlF1ZXN0aW9uKiogOgoKLSAgIFdlIGhhdmUgb25lIGdlbmUgcGVyIGxpbmUsIG9uZSBjZWxsIHBlciBjb2x1bW4sIGFuZCBSTkEgY291bnRzIGluIGVhY2ggcm93LgogICAgCiAgICBgYGB7ciBxX25vcm1zY2FsZmlsdCwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KICAgICMgcV9ub3Jtc2NhbGZpbHQKICAgIAogICAgIyMgQXJlIHRoZXNlIGNvdW50cyBub3JtYWxpemVkID8gc2NhbGVkID8gZmlsdGVyZWQgPwogICAgCiAgICBgYGAKICAgIAogICAgPGJyPgogICAgCiAgICBgYGB7ciBhX25vcm1zY2FsZmlsdCwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiAgICAjIGFfbm9ybXNjYWxmaWx0CiAgICAKICAgICMjIC4gVGhlc2UgY291bnRzIGFyZSBub3JtYWxpemVkLCBzY2FsZWQsIGZpbHRlcmVkIGZvciBzdXJlLgogICAgIyMKICAgICMjIC4gVGhpcyBpbmZvcm1hdGlvbiBpcyBhdmFpbGFibGUgaW4gdGhlIHNldXJhdCBvYmplY3QgaXRzZWxmLCAKICAgICMjICAgd2l0aGluIHRoZSBzbG90IGBAY29tbWFuZHNgLiAKICAgIAogICAgYGBgCiAgICAKICAgIFNlZSBhbiBleGFtcGxlIGJlbG93OgogICAgCiAgICBgYGB7ciBhX25vcm1zY2FsZmlsdDIsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQogICAgIyBhX25vcm1zY2FsZmlsdAogICAgCiAgICAjIyAuIFRoZXNlIGNvdW50cyBhcmUgbm9ybWFsaXplZCwgc2NhbGVkLCBmaWx0ZXJlZCBmb3Igc3VyZS4KICAgICMjCiAgICAjIyAuIFRvdGFsIGNvdW50cywgbWF4IHZhbHVlLCBjb3VudHMgcGVyIGNlbGwgYW5kIG51bWJlciBvZgogICAgIyMgICBleHByZXNzZWQgZmVhdHVyZXMgcGVyIGNlbGwsIGFsbCBoYXZlIGRlY3JlYXNlZC4KICAgICMjCiAgICAjIyAuIFRoaXMgaW5mb3JtYXRpb24gaXMgYXZhaWxhYmxlIGluIHRoZSBzZXVyYXQgb2JqZWN0IGl0c2VsZiwgCiAgICAjIyAgIHdpdGhpbiB0aGUgc2xvdCBgQGNvbW1hbmRzYC4gU2VlIGFuIGV4YW1wbGUgYmVsb3c6CiAgICAKICAgIGBgYAogICAgCiAgICBgYGB7ciBzZXVyYXRfaGlzdG9yeSwgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKX0KICAgICMgc2V1cmF0X2hpc3RvcnkKICAgIAogICAgbmFtZXMoc29iakBjb21tYW5kcykKICAgIAogICAgYGBgCiAgICAKICAgICoqSG93ZXZlcioqLCBwbGVhc2UgcmVtZW1iZXIgdGhhdCAgOgogICAgCiAgICAtICAgQ291bnRzIGluIHRoZSBzbG90IGBjb3VudGAgYXJlICoqcmF3IGNvdW50cyoqCiAgICAtICAgKipOb3JtYWxpemVkIGNvdW50cyoqIGFyZSBpbiB0aGUgc2xvdCBgZGF0YWAKICAgIC0gICAqKnNjYWxlZCoqIGRhdGEgYXJlIGluIHRoZSBzbG90IGBzY2FsZWQuZGF0YWAgKHRoaXMgb25lIHdhcyBkYW1uIG9idmlvdXMpLgogICAgCiAgICBBbmQgaXQgeW91IGRvIG5vdCBmaW5kIHRoYXQgY3J5c3RhbC1jbGVhciwgSSB0b3RhbGx5IGFncmVlIHdpdGggeW91LgoKICAgIDxicj4KCi0gICBSYXcgY291bnRzIGhhdmUgbWFueSwgc28gbWFueSB6ZXJvcyAuLi4KCiAgICBgYGB7ciBxX3NvemVybywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KICAgICMgcV9zb3plcm8KICAgIAogICAgIyMgSXMgaXQgbm9ybWFsIHRvIHlvdSA/CiAgICAKICAgIGBgYAogICAgCiAgICA8YnI+CiAgICAKICAgIGBgYHtyIGFfc296ZXJvLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KICAgICMgYV9zb3plcm8KICAgIAogICAgIyMgLiBUaGUgbGFyZ2UgbnVtYmVyIG9mIG51bGwgY291bnRzIGlzIGNvbXBsZXRlbHkgbm9ybWFsIGZvciB0aGlzCiAgICAjIyAgICJkcm9wbGV0IiB0ZWNobm9sb2d5LiBJbiBtYXRocy9zdGF0cywgd2UgdGFsayBhYm91dCAibWF0cml4IHNwYXJjaXR5IiwKICAgICMjICAgaS5lIGEgdGFibGUgd2l0aCBsb3RzIChsb3RzISkgb2YgemVyb3MuCgogICAgYGBgCgogICAgPGJyPgogICAgCi0gICBBbmQgY291bnQgdmFsdWVzIGFyZSBzbyBsb3cgLi4uCgogICAgYGBge3IgcV9sb3djb3VudHMsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24ifQogICAgIyBxX2xvd2NvdW50cwogICAgCiAgICAjIyBXaHkgPyBJcyBpdCBiZWNhdXNlIHdlIGRvd25zYW1wbGVkIHRoZSByZWFkIGRlcHRoIGZvciB0aGlzIHRyYWluaW5nID8KICAgIAogICAgYGBgCiAgICAKICAgIDxicj4KICAgIAogICAgYGBge3IgYV9sb3djb3VudHMsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQogICAgIyBhX2xvd2NvdW50cwogICAgCiAgICAjIyAuIE5vLiBPbmNlIGFnYWluLCB0aGlzIGlzIGludHJpbnNpYyB0byB0aGUgdGVjaG5vbG9neSB1c2VkLgogICAgIyMKICAgICMjIC4gSWYgdGhlIGRhdGEgd2VyZSB0byBiZSBkb3duc2FtcGxlZCwgd2Ugd291bGQgaGFkIGRvbmUgdGhpcyBieSBjcm9wcGluZwogICAgIyMgICB0aGUgY291bnQgbWF0cml4IG92ZXIgYSBzbWFsbCBjaHJvbW9zb21lLCBvciBhIHJlZHVjZWQgZ2VuZSBlc3QsIG5vdCAKICAgICMjIGJ5IGxvd2VyaW5nIHRoZSBnbG9iYWwgYW1vdW50IG9mIHJlYWRzIChkZXB0aCkuCgogICAgYGBgCgojIFNlbGVjdCBhIERFIG1ldGhvZAoKIyMgQXZhaWxhYmxlIG1ldGhvZHMKCltTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvZGVfdmlnbmV0dGUpIGxldCB1cyB1c2UgbXVsdGlwbGUgCmRpZmZlcmVudGlhbCBhbmFseXNpcyBtZXRob2RzIHdpdGggaXRzIGZ1bmN0aW9uIFtgRmluZE1hcmtlcnNgXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3JlZmVyZW5jZS9maW5kbWFya2VycykuCgoxLiBbd2lsY294XShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcnN0YXRpeC92ZXJzaW9ucy8wLjcuMSk6IFRoZSB3aWxjb3hvbiB0ZXN0IHRlc3RzIHRoZSBtZWFuIG9mIGV4cHJlc3Npb24gYW5kIGxvb2tzIGZvciBhIGRpZmZlcmVuY2UgaW4gdGhlc2UgbWVhbnMuCjEuIFtNQVNUXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvTUFTVC9pbnN0L2RvYy9NQVNULUludHJvLmh0bWwpOiBUaGlzIHRvb2wgaGFzIGJlZW4gYnVpbHQgZm9yIFNpbmdsZSBDZWxsLiBJdCBpcyBiYXNlZCBvbiBhIHN0YXRpc3RpY2FsIG1vZGVsIGNhbGxlZCBbIkh1cmRsZSBNb2RlbCJdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0h1cmRsZV9tb2RlbCksIHdoaWNoIGV4Y2VsbHMgd2l0aCBkYXRhIHRoYXQgY29udGFpbnMgbG90cyBvZiB6ZXJvcyAod2hpY2ggaXMgb3VyIGNhc2UgaW4gU2luZ2xlIENlbGwgUk5BLVNlcTogbW9zdCBvZiB0aGUgZ2VuZXMgYXJlICpub3QqIGV4cHJlc3NlZC4pCjEuIFtERVNlcTJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNyZWNvbW1lbmRhdGlvbnMtZm9yLXNpbmdsZS1jZWxsLWFuYWx5c2lzKTogVGhpcyB0b29sIGhhcyBvcmlnaW5hbGx5IGJlZW4gYnVpbHQgZm9yIGJ1bGsgUk5BLVNlcSBidXQgbm93IGluY2x1ZGVzIHNwZWNpZmljIGZ1bmN0aW9ucyBmb3IgU2luZ2xlIENlbGwuIEl0IHBlcmZvcm1zIHdlbGwgd2hlbiBjb3VudHMgYXJlIGhpZ2hseSB2YXJpYWJsZSBvciB3aGVuIHlvdSB3YW5kIHRvIGNvbXBhcmUgYSBoYW5kZnVsIG9mIGNlbGxzLgoxLiBbdC10ZXN0XShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy42LjIvdG9waWNzL3QudGVzdCk6IFRoZSB0LXRlc3QgdXNlcyBhIGNvbXBhcmlzb24gb2YgbWVhbnMgdG8gYXNzZXNzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHByb2JhYmlsaXR5LgoxLiBbcm9jXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SZWNlaXZlcl9vcGVyYXRpbmdfY2hhcmFjdGVyaXN0aWMpOiBJbiB0aGlzIG1ldGhvZCwgYW4gQVVDIHZhbHVlIG9mIGAxYCBtZWFucyB0aGF0IGV4cHJlc3Npb24gdmFsdWVzIGZvciB0aGlzIGdlbmUgYWxvbmUgY2FuIHBlcmZlY3RseSBjbGFzc2lmeSB0aGUgdHdvIGdyb3VwaW5ncywgaS5lIDogZWFjaCBvZiB0aGUgY2VsbHMgaW4gImNlbGxzLjEiIChjb25kaXRpb24gMSkgZXhoaWJpdCBhIGhpZ2hlciBsZXZlbCB0aGFuIGVhY2ggb2YgdGhlIGNlbGxzIGluICJjZWxscy4yIiAoY29uZGl0aW9uIDIpLiBBbiBBVUMgdmFsdWUgb2YgYDBgIGFsc28gbWVhbnMgdGhlcmUgaXMgcGVyZmVjdCBjbGFzc2lmaWNhdGlvbiwgYnV0IGluIHRoZSBvcHBvc2l0ZSBkaXJlY3Rpb24gKGhpZ2hlciBpbiBjb25kaXRpb24gMiB0aGFuIGluIDEpLiBBIHZhbHVlIG9mIGAwLjVgIGltcGxpZXMgdGhhdCB0aGUgZ2VuZSBoYXMgbm8gcHJlZGljdGl2ZSBwb3dlciB0byBjbGFzc2lmeSB0aGUgdHdvIGdyb3Vwcy4KClRoZSBtYWluIHF1ZXN0aW9uIG5vdyBpcyAqKmhvdyB0byBjaG9vc2UgdGhlIHJpZ2h0IHRlc3QgPz8qKgoKKipTcG9pbGVycyoqIDogdGhlcmUgYXJlIG5vIG9wdGlvbiBiZXR0ZXIgdGhhbiBhbm90aGVyIGluIGFsbCB3YXlzICEgVGhhbmsgeW91IGZvciB5b3VyIGF0dGVudGlvbiwgeW91IGNhbiBnbyBiYWNrIGhvbWUgbm93LgoKPGJyPgoKRnJvbSBTb25lc29uICYgUm9iaW5zb24gKDIwMTgpIE5hdHVyZSBNZXRob2RzOgoKIVtkZV90b29sc10oaW1hZ2VzL0RFX3Rvb2xzLnBuZykKCkhlcmUsIHJlc2VhcmNoZXJzIGhhdmUgZGVzaWduZWQgYW4gKiphcnRpZmljaWFsIGRhdGFzZXQqKiB3aGVyZSB0aGV5IGtuZXcgaW4gYWR2YW5jZSB0aGUgbGlzdCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZXkgaGF2ZSB1c2VkIGFsbCB0aGVzZSBhbGdvcml0aG1zIGFuZCBjb25zaWduZWQgdGhlIHJlc3VsdHMuCgoxLiBgREVTZXEyYCwgYExpbW1hYCBzZWVtIHRvIGhhdmUgYSBoaWdoZXIgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyAoZ2VuZXMgaWRlbnRpZmllZCBhcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgd2hpbGUgdGhleSB3ZXJlIGFjdHVhbGx5IG5vdC4pCjEuIGBXaWxjb3hvbmAgc2VlbXMgdG8gYmUgYmV0dGVyIGluIGdlbmVyYWwuCjEuIGBNQVNUYCBwZXJmb3JtcyB3ZWxsIGluIGFic29sdXRlCgpgQU5PVkFgIHdhcyBub3QgcHJlc2VudCBpbiB0aGlzIHN0dWR5IChidXQgcHJvYmFibHkgd291bGQgaGF2ZSBwZXJmb3JtZWQgc2ltaWxhcmx5IHRvIGB0LXRlc3RgLgoKIyMgQSBjYXNlOiB0aGUgYFBsYWM4YCBnZW5lCgpUaGUgcXVlc3Rpb24gaXMgbm93IHRvIGd1ZXNzIHdoZXRoZXIgdGhpcyBnZW5lIGlzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCwgb3Igbm90LgoKIyMjIENlbGwgb2JzZXJ2YXRpb25zCgpMZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgZ2VuZSBuYW1lZCBbJ1BsYWM4J10oaHR0cHM6Ly93d3cuZ2VuZWNhcmRzLm9yZy9jZ2ktYmluL2NhcmRkaXNwLnBsP2dlbmU9UExBQzgma2V5d29yZHM9cGxhYzgpCgpJdCBpcyBrbm93biB0byBiZSBpbnZvbHZlZCBpbiBwb3NpdGl2ZSByZWd1bGF0aW9uIG9mIGNvbGQtaW5kdWNlZCB0aGVybW9nZW5lc2lzLAphbmQgcG9zaXRpdmUgcmVndWxhdGlvbiBvZiB0cmFuc2NyaXB0aW9uIGJ5IFJOQSBwb2x5bWVyYXNlIElJLiAKCkluIG9yZGVyIHRvIHBsb3QgaXRzIGV4cHJlc3Npb24gYWNyb3NzIGFsbCBjZWxscywgd2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgCmZ1bmN0aW9uIFtgVmxuUGxvdGBdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvcmVmZXJlbmNlL3ZsbnBsb3QpCmZyb20gdGhlIGBTZXVyYXRgIHBhY2thZ2UuIAoKVGhlIGlucHV0IG9iamVjdCBpcyBvYnZpb3VzbHkgY29udGFpbmVkIGluIHRoZSBgc29iamAgdmFyaWFibGUsIHNpbmNlIGl0IGlzIApvdXIgb25seSBTZXVyYXQgb2JqZWN0LiBJbiBhZGRpdGlvbiwgd2UgYXJlIGdvaW5nIHRvIHNlbGVjdCB0aGUgZmVhdHVyZSBgUGxhYzhgLCAKYW5kIHNwbGl0IHRoZSBncmFwaCBhY2NvcmRpbmcgdG8gdGhlIGNsdXN0ZXJzIHdlIGFubm90YXRlZCBlYXJsaWVyLgoKCmBgYHtyIHNldXJhdF92bG5wbG90X1BsYWM4X2RlbW8sIGV2YWw9VFJVRSwgZWNobz1UUlVFfQojIHNldXJhdF92bG5wbG90X1BsYWM4X2RlbW8KClNldXJhdDo6VmxuUGxvdCgKICAjIEEgc3Vic2V0IG9mIHRoZSBTZXVyYXQgb2JqZWN0CiAgIyBsaW1pdGVkIHRvIGNsdXN0ZXJzIDggYW5kIDEwLCAKICAjIG9yIGVsc2Ugd2Ugd2lsbCBwbG90IGFsbCB0aGUgY2x1c3RlcnMKICBvYmplY3QgPSBzdWJzZXQoc29iaiwgSGFybW9ueVN0YW5kYWxvbmVfY2x1c3RlcnMgJWluJSBjKCI4IiwgIjEwIikpLAogIAogICMgVGhlIG5hbWUgb2YgdGhlIGdlbmUgb2YgaW50ZXJlc3QgKGZlYXR1cmUgPSBnZW5lKQogIGZlYXR1cmVzID0gIlBsYWM4IiwKICAKICAjIFRoZSBuYW1lIG9mIHRoZSBTZXVyYXQgY2VsbCBhbm5vdGF0aW9uCiAgc3BsaXQuYnkgPSAiSGFybW9ueVN0YW5kYWxvbmVfY2x1c3RlcnMiLAogIAogICMgQ2hhbmdlIGNvbG9yIGZvciBwcmVzZW50YXRpb24KICBjb2xzID0gYygiZGFya3NsYXRlZ3JheTMiLCAib2xpdmVkcmFiIikKICAKKQpgYGAKCgpgYGB7ciBxX2lzcGxhYzhkZSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2lzcGxhYzhkZQoKIyMgVXNpbmcgeW91ciBpbnR1aXRpb24sIGlzIHRoaXMgZ2VuZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgCiMjIGJldHdlZW4gY2x1c3RlcnMgOCBhbmQgMTAgPwoKYGBgCgoKYGBge3IgYV9pc3BsYWM4ZGUsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIHFfaXNwbGFjOGRlCgojIyAuIEluIGNsdXN0ZXIgMTAsIHRoZSB2aW9saW4gcGxvdCBoaWdobGlnaHRzIGFsbW9zdCBubyBjZWxscyB3aXRoIGxvdyBvciAKIyMgICB6ZXJvIGBQbGFjOGAgZXhwcmVzc2lvbi4gVGhlIGhpZ2hlc3QgZGVuc2l0eSBvZiBjZWxscyBoYXMgYSBgUGxhYzhgIAojIyAgIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBhcm91bmcgfjEuNS4KIyMKIyMgLiBJbiBjbHVzdGVyIDgsIGNlbGxzIHNlZW0gdG8gaGF2ZSBubyBleHByZXNzaW9uIG9mIGBQbGFjOGAgYXQgYWxsLgojIwojIyAuIElNSE8gKGZlZWwgZnJlZSB0byBkaXNhZ3JlZSksIHRoZSBleHByZXNzaW9uIG9mIHRoZSBnZW5lIGBQbGFjOGAgZGlmZmVycyAKIyMgYmV0d2VlbiBvdXIgY2x1c3RlcnMgOCBhbmQgMTAuIFRoaXMgaXMgcHVyZWx5IGludHVpdGl2ZS4KCmBgYAoKPGJyPgoKTm93LCBsZXQncyBjaGVjayB0aGUgc2FtZSBnZW5lIGV4cHJlc3Npb24sIGJ1dCBjb21wYXJpbmcgY2VsbHMgdGFnZ2VkIGFzIGluIAp0aGUgRzEgYW5kIFMgcGhhc2UuCgpgYGB7ciB2bG5wbG90X3NldXJhdF9ncm91cF9waGFzZV9jb2RlLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0KIyB2bG5wbG90X3NldXJhdF9ncm91cF9waGFzZV9jb2RlCgpTZXVyYXQ6OlZsblBsb3QoCiAgIyBUaGUgU2V1cmF0IG9iamVjdAogIG9iamVjdCA9IHNvYmosCiAgIyBUaGUgbmFtZSBvZiB0aGUgZ2VuZSBvZiBpbnRlcmVzdCAoZmVhdHVyZSA9IGdlbmUpCiAgZmVhdHVyZXMgPSAiUGxhYzgiLAogICMgVGhlIG5hbWUgb2YgdGhlIFNldXJhdCBjZWxsLWN5Y2xlIGFubm90YXRpb24KICBncm91cC5ieSA9ICJDQ19TZXVyYXRfUGhhc2UiLAogICMgQ2hhbmdlIGNvbG9yIGZvciBwcmVzZW50YXRpb24KICBjb2xzID0gYygiZGFya3NsYXRlZ3JheTMiLCAib2xpdmVkcmFiIiwgIm9yYW5nZXJlZDMiKQopCgpgYGAKCgpgYGB7ciBxX2lzcGxhYzhkZUcxUywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2lzcGxhYzhkZUcxUwoKIyMgVXNpbmcgeW91ciBpbnR1aXRpb24sIGlzIHRoaXMgZ2VuZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgCiMjIGJldHdlZW4gdGhlIEcxIGFuZCBTIGNlbGwgY3ljbGUgcGhhc2VzID8KCmBgYAoKYGBge3IgYV9pc3BsYWM4ZGVHMVMsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIGFfaXNwbGFjOGRlRzFTCgojIyAuIFRoaXMgb25lIHNlZW1zIG1vcmUgdHJpY2t5Li4uIE9uZSBjYW4gb2JzZXJ2ZSB0aGF0IG1vc3Qgb2YgdGhlIGV4cHJlc3Npb24KIyMgICBpcyBudWxsLCBzb21lIGNlbGxzIGV4cHJlc3MgdGhlIGdlbmUuCiMjCiMjIC4gV2UgbmVlZCB0byBpbnZlc3RpZ2F0ZSBhIGJpdCBkZWVwZXIgIQoKYGBgCgoKT2theSwgbGV0J3MgZ2V0IHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgdGhlc2UgZXhwcmVzc2lvbiBkaXN0cmlidXRpb25zLgoKYGBge3IgZ2VuZXJhbF9jb3VudF90YWJsZSwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiMgZ2VuZXJhbF9jb3VudF90YWJsZQoKIyMgU3RvcmUgY291bnRzIGluIGEgdmFyaWFibGUKY291bnRzIDwtIGJhc2U6OmFzLmRhdGEuZnJhbWUoCiAgIyBUaGUgbWF0cml4IHRvIHJlZm9ybWF0IGludG8gYSBkYXRhZnJhbWUKICB4ID0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoCiAgICBvYmplY3QgPSBzb2JqLCAKICAgIGFzc2F5ID0gIlJOQSIsIAogICAgbGF5ZXIgPSAiZGF0YSIpCikKCiMjIFJlbmFtZSBjZWxscyB3aXRoIGNlbGwgaGFybW9ueSBjbHVzdGVyCmJhc2U6OmNvbG5hbWVzKGNvdW50cykgPC0gcGFzdGUoCiAgIyBUaGUgbmFtZXMgb2YgdGhlIGNlbGwgY2x1c3RlciBmb3IgZWFjaCBjZWxsCiAgc29iaiRDQ19TZXVyYXRfUGhhc2UsCiAgIyBUaGUgbmFtZXMgb2YgdGhlIGNlbGxzIHRoZW1zZWx2ZXMKICBjb2xuYW1lcyhzb2JqKSwKICBzZXAgPSAiXyIKKQoKYGBgCgpMZXQncyBjaGVjayBvdXIgYGNvdW50c2AgdmFyaWFibGUgOgoKYGBge3IgZ2VuZXJhbF9jb3VudF90YWJsZV9kaXNwbGF5LCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CiMgZ2VuZXJhbF9jb3VudF90YWJsZV9kaXNwbGF5CgpEVDo6ZGF0YXRhYmxlKGhlYWQoY291bnRzKSkKCmBgYAoKPGRpdiBjbGFzcz0iY29sdW1uLWxlZnQiPgoKV2UgaGF2ZSBgciBsZW5ndGgoY29sbmFtZXMoc29ialssIHNvYmokQ0NfU2V1cmF0X1BoYXNlID09ICJHMSJdKSlgIGNlbGxzIHdpdGhpbiB0aGUgYEcxIGdyb3VwYCA6CgpgYGB7ciBQbGFjOF9zdW1tYXJpZXNfRzEsIGV2YWw9VFJVRX0KIyBQbGFjOF9zdW1tYXJpZXNfRzEKCmNvdW50c0cxIDwtIHNlbGVjdChjb3VudHMsIG1hdGNoZXMoIl5HMS4iKSkKClBsYWM4RzEgPC0gYmFzZTo6YXMuZGF0YS5mcmFtZShiYXNlOjp0KGNvdW50c0cxWyJQbGFjOCIsIF0pKQoKYmFzZTo6c3VtbWFyeShQbGFjOEcxKQoKYGBgCgo8L2Rpdj4KCjxkaXYgY2xhc3M9ImNvbHVtbi1yaWdodCI+CgpXZSBoYXZlIGByIGxlbmd0aChjb2xuYW1lcyhzb2JqWywgc29iaiRDQ19TZXVyYXRfUGhhc2UgPT0gIlMiXSkpYCBjZWxscyB3aXRoaW5nIHRoZSBgUyBncm91cGAgOgoKYGBge3IgUGxhYzhfc3VtbWFyaWVzX1MsIGV2YWw9VFJVRX0KIyBQbGFjOF9zdW1tYXJpZXNfUwoKY291bnRzUyA8LSBzZWxlY3QoY291bnRzLCBtYXRjaGVzKCJeUy4iKSkKClBsYWM4UyA8LSBiYXNlOjphcy5kYXRhLmZyYW1lKGJhc2U6OnQoY291bnRzU1siUGxhYzgiLCBdKSkKCmJhc2U6OnN1bW1hcnkoUGxhYzhTKQoKYGBgCgo8L2Rpdj4KCiMjIyBGcm9tIGJpb2xvZ3kgdG8gc3RhdGlzdGljcwoKT2theSwgbGV0IHVzIHJlc29ydCBvbiBzdGF0aXN0aWNzIHRvIGV2YWx1YXRlIG91ciBjaGFuY2VzIHRvIGJlIGd1ZXNzIApjb3JyZWN0bHksIG9yIG91ciByaXNrcyB0byBndWVzcyB3cm9uZy4KCldlIGhhdmUgbG90cyBvZiBvYnNlcnZhdGlvbnMgOgoKLSAgIGByIGJhc2U6Omxlbmd0aChiYXNlOjpjb2xuYW1lcyhzb2JqWywgc29iaiRDQ19TZXVyYXRfUGhhc2UgPT0gIkcxIl0pKWAgCmNlbGxzIHdpdGhpbiB0aGUgRzEgcGhhc2UKLSAgIGByIGJhc2U6Omxlbmd0aChiYXNlOjpjb2xuYW1lcyhzb2JqWywgc29iaiRDQ19TZXVyYXRfUGhhc2UgPT0gIlMiXSkpYCAKY2VsbHMgd2l0aGluIHRoZSBTIHBoYXNlCgpTdGF0aXN0aWNpYW5zIHJlYWxseSBsaWtlIHRvIGhhdmUgKiphIGxvdCBvZiBvYnNlcnZhdGlvbnMqKiAhCgpJZGVhbGx5LCBzdGF0aXN0aWNpYW5zIG5ldmVyIGhhdmUgZW5vdWdoIG9ic2VydmF0aW9ucywgYnV0IGF0IGxlYXN0IHRoZXkgIHByZWZlcgp3aGVuIHRoZXkgaGF2ZSAqKm1vcmUgdGhhbiB0ZXN0cyoqLgoKSGVyZSwgd2UgaGF2ZSBhIHRvdGFsIG9mIGByIGJhc2U6Omxlbmd0aChiYXNlOjpjb2xuYW1lcyhzb2JqWywgc29iaiRDQ19TZXVyYXRfUGhhc2UgPT0gIkcxIl0pKSArIGJhc2U6Omxlbmd0aChiYXNlOjpjb2xuYW1lcyhzb2JqWywgc29iaiRDQ19TZXVyYXRfUGhhc2UgPT0gIlMiXSkpYCAKb2JzZXJ2YXRpb25zIGFuZCB3ZSBhcmUgdGVzdGluZyBhIHNpbmdsZSBnZW5lLgoKV2UgbGl2ZSBpbiBhIHN0YXRpc3RpY2lhbidzIHdldCBkcmVhbSAhCgpOb3csIGlmIHdlIHRoaW5rIGxpa2UgYSBzdGF0aXN0aWNpYW4sIHdlIGNhbiBhc2sgb3Vyc2VsdmVzIDoKCi0gICBBcmUgb3VyIGNlbGxzIHN1cHBvc2VkIHRvIGJlIGludGVyYWN0aW5nIHdpdGggZWFjaCBvdGhlcnMgPyAKLSAgIE9yIGFyZSB0aGV5IGluZGVwZW5kZW50IGZyb20gZWFjaCBvdGhlcnMgPyAKLSAgIERvZXMgdGhlIGV4cHJlc3Npb24gaW4gb3VyIGNlbGxzIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24gPwoKQWxsIG9mIHRoaXMgaXMgdmVyeSBpbXBvcnRhbnQsIGFuZCB1c3VhbGx5LCBpdCByZXF1aXJlcyBhIGRpc2N1c3Npb24uCgpGb3IgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24sIHRoZXJlIGlzIGFuIGVhc3kgY2hlY2suCkxldCdzIGRyYXcgdGhlIGV4cHJlc3Npb24gbGlrZSB3ZSBkaWQgYWJvdmUuCgpGaXJzdCwgd2UgdXNlIHRoZSBmdW5jdGlvbiBbYHJiaW5kYF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy42LjIvdG9waWNzL2NiaW5kKSAKZnJvbSBgYmFzZWAgcGFja2FnZS4KQmUgY2FyZWZ1bCwgdGhlIGZ1bmN0aW9uIGByYmluZGAgYWxzbyBleGlzdHMgCmluIGBEZWxheWVkQXJyYXlgLCBgZGF0YS50YWJsZWAsIGFuZCBgQmlvY0dlbmVyaWNzYCBwYWNrYWdlcyAhCldlIHdhbnQgdG8gdXNlIHRoZSAiYmFzaWMiIG9uZS4KCmBgYHtyIGRpc3RyaWJ1dGlvbl9QbGFjOF90YWJsZV9idWlsZCwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiMgZGlzdHJpYnV0aW9uX1BsYWM4X3RhYmxlX2J1aWxkCgojIyBBZGQgY29sdW1uIGlkaWVudGlmaWVycyBpbiBlYWNoIGNvdW50IGRhdGFmcmFtZXMKUGxhYzhHMSRwaGFzZSA8LSAiRzEiClBsYWM4UyRwaGFzZSA8LSAiUyIKIyMgUGFzdGUgdGhlIHJvd3MgYmVuZWl0aCBlYWNoIG90aGVyClBsYWM4IDwtIGJhc2U6OnJiaW5kKAogICMjICB2YXJpYWJsZSBwb2ludGluZyB0byBHMSBjb3VudHMKICBQbGFjOEcxLAogICMjICB2YXJpYWJsZSBwb2ludGluZyB0byBTIGNvdW50cwogIFBsYWM4UywKICAjIyBBIEJvb2xlYW4sIHJlcXVlc3RpbmcgdGhhdCBzdHJpbmdzL2NoYXJhY3RlcnMKICAjIyBzaG91bGQgbm90IGJlIGNhc3RlZCBhcyBgbG9naWNhbGAuIEl0IGJyZWFrcyBncmFwaHMuCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCgogICkKYGBgCgpTZWNvbmRseSwgd2UgdXNlIHRoZSBmdW5jdGlvbiBbYGdnaGlzdG9ncmFtYF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dncHVici92ZXJzaW9ucy8wLjYuMC90b3BpY3MvZ2doaXN0b2dyYW0pIGZyb20gdGhlIHBhY2thZ2UgYGdncHVicmAgaW4gb3JkZXIgdG8gZGlzcGxheSByZWxhdGl2ZSBhYnVuZGFuY2Ugb2YgZ2VuZSBleHByZXNzaW9uIDoKCmBgYHtyIGRpc3RyaWJ1dGlvbl9QbGFjOF9kaXNwbGF5LCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBkaXN0cmlidXRpb25fUGxhYzhfZGlzcGxheQoKZ2dwdWJyOjpnZ2hpc3RvZ3JhbSgKICBQbGFjOCwKICB4ID0gIlBsYWM4IiwKICB5ID0gJy4uZGVuc2l0eS4uJywKICBmaWxsID0gInN0ZWVsYmx1ZSIsCiAgYmlucyA9IDE1LAogIGFkZF9kZW5zaXR5ID0gVFJVRQopCgpgYGAKCgpPdXIgZGlzdHJpYnV0aW9uIGRvZXNuJ3Qgc2VlbSB0byBiZSBub3JtYWwsIG5vciBiaW5vbWlhbCAuLi4gd2Ugd2lsbCBoYXZlIHRvIApyZWx5IG9uICoqbm9uLXBhcmFtZXRyaWMqKiB0ZXN0cy4KCkxldCdzIHJ1biBhIG5vbi1wYXJhbWV0cmljIHRlc3QgYmFzZWQgb24gdGhlIG1lYW4gb2YgZGlzdHJpYnV0aW9ucywgCnNpbmNlIGl0J3MgdGhlIGNsb3RoZXN0IHRvIG91ciAnaW50dWl0aXZlJyBhcHByb2FjaC4gTGV0IHRoZXJlIGJlIApbV2lsY294b25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dpbGNveG9uX3NpZ25lZC1yYW5rX3Rlc3QpIHRlc3QuCgpJbiBSLCBpdCdzIHF1aXRlIHN0cmFpZ2h0Zm9yd2FyZCA6IHdlIGhhdmUgdGhlIGZ1bmN0aW9uIApbYHdpbGNveG9uX3Rlc3RgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcnN0YXRpeC92ZXJzaW9ucy8wLjcuMSkgCnRvIHBlcmZvcm0gdGhlIHRlc3QsIHRoZW4gd2UgY2FuIHBsb3QgdGhlIHJlc3VsdC4KCmBgYHtyIHdpbGNveG9uX1BsYWM4LCBldmFsID0gVFJVRSwgZWNobz1UUlVFfQojIHdpbGNveG9uX1BsYWM4CgojIyBPbiB0aGUgZXhwcmVzc2lvbiB0YWJsZSBzdG9yZWQgaW4gdGhlIHZhcmlhbGJlIGBQbGFjOGAsCiMjIGZpcnN0IGFwcGx5IHRoZSBmdW5jdGlvbiBgd2lsY294X3Rlc3RgIGZyb20gcGFja2FnZSBgcnN0YXRpeGAsCiMjIHRoZW4gd2UgYXBwbHkgdGhlIGZ1bmN0aW9uIGBhZGRfc2lnbmlmaWNhbmNlYCBmcm9tIHBhY2thZ2UgYHJzdGF0aXhgCnN0YXQudGVzdCA8LSBQbGFjOCAlPiUgcnN0YXRpeDo6d2lsY294X3Rlc3QoUGxhYzggfiBwaGFzZSkgJT4lIHJzdGF0aXg6OmFkZF9zaWduaWZpY2FuY2UoKQoKIyBXaGlsZSBkb2luZyBzbywgd2UgdXN1YWxseSBhbHNvIGNvbXB1dGUgZWZmZWN0IHNpemUKZWZmLnNpemUgPC0gUGxhYzggJT4lIHJzdGF0aXg6OndpbGNveF9lZmZzaXplKFBsYWM4IH4gcGhhc2UpCgpgYGAKCmBgYHtyIGRpc3BsYXlfd2lsY294b25fUGxhYzhfcmVzdWx0LCBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFfQojIGRpc3BsYXlfd2lsY294b25fUGxhYzhfcmVzdWx0CgpEVDo6ZGF0YXRhYmxlKHN0YXQudGVzdCwgY2FwdGlvbiA9ICJXaWxjb3hvbiB0ZXN0IHJlc3VsdCIpCnN0YXQudGVzdCA8LSBQbGFjOCAlPiUgcnN0YXRpeDo6dF90ZXN0KFBsYWM4IH4gcGhhc2UpICU+JSByc3RhdGl4OjphZGRfc2lnbmlmaWNhbmNlKCkKCmBgYAoKV2lsY294b24gdGVzdCBzYXlzOiB0aGUgZGlzdHJpYnV0aW9ucyBhcmUgZGlmZmVyZW50LCB3aXRoIGEgYHIgc3RhdC50ZXN0JHAgKiAxMDBgIAolIG9mIHJpc2tzIG9mIGJlaW5nIHdyb25nLiBUaGUgZ2VuZSBgUGxhYzhgIGNhbiBzYWZlbHkgYmUgc2FpZCAqKmRpZmZlcmVudGlhbGx5IApleHByZXNzZWQqKi4KCldlIGNhbiBjb21wdXRlIGEgZm9sZCBjaGFuZ2UgYW5kIGNvbmNsdWRlLgoKKipOT1RFKiogOiBKdXN0IG91dCBvZiBjdXJpb3NpdHksIGJlIGF3YXJlIHRoYXQgW2B0X3Rlc3RgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcnN0YXRpeC92ZXJzaW9ucy8wLjcuMi90b3BpY3MvdF90ZXN0KQpmcm9tIGByc3RhdGl4YCBwYWNrYWdlLCBnaXZlcyB0aGUgc2FtZSBhbnN3ZXIgKHdpdGggZGlmZmVyZW50IHNpZ25pZmljYW5jZSkuCkhvd2V2ZXIsIFtgREVTZXFgXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3JlZmVyZW5jZS9maW5kbWFya2VycykgZ2l2ZXMgCmEgcC12YWx1ZSBvZiAwLjA1IGFuZCBhbiBhZGp1c3RlZCBwLXZhbHVlIGVxdWFsIHRvIDEgIQoKPGJyPgoKRGVwZW5kaW5nIG9uIHRoZSB0ZXN0IHVzZWQsIHRoaXMgZ2VuZSBpcywgb3IgaXMgbm90IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4KCgojIyBDb25jbHVzaW9uCgpJbiBjb25jbHVzaW9uLCB0byBzZWxlY3QgeW91ciBERUEgbWV0aG9kLCB1c2UgdGhlIGZvbGxvd2luZyBydWxlcyBvZiB0aHVtYiA6CgoxLiBJZiB5b3UgaGF2ZSBhbHJlYWR5IGRvbmUgYSBzdHVkeSB3aXRoIG9uZSBvZiB0aGVzZSBtZXRob2RzLCBrZWVwIHVzaW5nIAogICB0aGUgc2FtZS4gVGhpcyBpcyBjcnV0aWFsIGlmIHlvdSBldmVyIHdhbnQgdG8gY29tcGFyZSB5b3VyIG5ldyByZXN1bHQgd2l0aCAKICAgdGhlIG9sZCBvbmVzLgoyLiBJZiB5b3Ugd2FudCB0byBjb21wYXJlIHlvdXIgc3R1ZHkgd2l0aCBhIHB1Ymxpc2hlZCBvbmUsIHVzZSB0aGUgc2FtZSBtZXRob2QuCjMuIElmIHlvdSBoYXZlIG5vIGlkZWEsIHVzZSBXaWxjb3hvbi4KNC4gSWYgeW91IGhhdmUgKipidWxrKiogZGF0YSBhbmFseXplZCB3aXRoIGBERVNlcTJgL2BMaW1tYWAsIHVzZSAKICAgYERFU2VxMmAvYExpbW1hYC4gSXQgd2lsbCBiZSBlYXNpZXIgdG8gdGFrZSBib3RoIHdvcmtzIGluIGNvbnNpZGVyYXRpb24uCgoqKlBsZWFzZSwgbmV2ZXIgZXZlciB1c2UgYSBzaW1wbGUgV2lsY294b24gb24gYnVsayBSTkEtU2VxIGRhdGEgISEqKgoKIyMgTkIgTUFTVCBERQoKTUFTVCB1c2VzIGEgaHVyZGxlIG1vZGVsIGRpdmlkZWQgaW50byB0d28gcGFydHM6CgotICAgRGlzY3JldGUg4oCTIG1vZGVscyB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIGZlYXR1cmUgaXMgZGV0ZWN0ZWQgaW4gYSBjZWxsIChwcmVzZW50IG9yIG5vdCkuCi0gICBDb250aW51b3VzIOKAkyBtb2RlbHMgdGhlIGxldmVsIG9mIGV4cHJlc3Npb24gZm9yIHRoZSBmZWF0dXJlIGluIGNlbGxzIHdoZXJlIGl0IGlzIGRldGVjdGVkLgoKTUFTVCBhbHNvIHVzZXMgdGhlIENlbGx1bGFyIERldGVjdGlvbiBSYXRlIChDRFIpIGFzIGEgY292YXJpYXRlIHRvIGFkanVzdCBmb3Igc2VxdWVuY2luZyBlZmZlY3RzLCBjYWxjdWxhdGVkIGFzIHRoZSBmcmFjdGlvbiBvZiBkZXRlY3RlZCBnZW5lcyBwZXIgY2VsbCAKKG5GZWF0dXJlIGJhcmNvZGUgPjAgLyBuSFZHKS4gSXQgaXMgYWxzbyBwb3NzaWJsZSB0byBpbmNsdWRlIGFkZGl0aW9uYWwgY292YXJpYXRlcywgc3VjaCBhcyBiYXRjaCBvciBleHBlcmltZW50YWwgbWV0cmljcywgd2hlbiBhbmFseXppbmcgbW9yZSB0aGFuIHR3byBzYW1wbGVzLgoKIyBTZWxlY3QgYSBkYXRhc2V0CgojIyBEYXRhc2V0IGRlcGVuZHMgb24gc2VsZWN0ZWQgbWV0aG9kCgpUaGVyZSBpdCBpcyBxdWl0ZSBlYXNpZXI6CgpgYGB7ciBjaG9vc2VfY291bnRzLCBldmFsPVRSVUUsIHJlc3VsdHM9J2FzaXMnLCBlY2hvPUZBTFNFfQpjaG9vc2VfY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodChkYXRhLmZyYW1lKAogIHdpbGNveCA9IGMoIk5vcm1hbGl6ZWQgY291bnRzIiwgInNvamJAYXNzYXlzW1snUk5BJ11dQGRhdGEiKSwKICB0X3Rlc3QgPSBjKCJOb3JtYWxpemVkIGNvdW50cyIsICJzb2piQGFzc2F5c1tbJ1JOQSddXUBkYXRhIiksCiAgUk9DID0gYygiTm9ybWFsaXplZCBjb3VudHMiLCAic29qYkBhc3NheXNbWydSTkEnXV1AZGF0YSIpLAogIEFOT1ZBID0gYygiTm9ybWFsaXplZCBjb3VudHMiLCAic29qYkBhc3NheXNbWydSTkEnXV1AZGF0YSIpLAogIE1BU1QgPSBjKCJSYXcgY291bnRzIiwgInNvamJAYXNzYXlzW1snUk5BJ11dQGNvdW50cyIpLAogIERFU2VxMiA9IGMoIlJhdyBjb3VudHMiLCAic29qYkBhc3NheXNbWydSTkEnXV1AY291bnRzIiksCiAgTGltbWEgPSBjKCJSYXcgY291bnRzIiwgInNvamJAYXNzYXlzW1snUk5BJ11dQGNvdW50cyIpCikpKQpjb2xuYW1lcyhjaG9vc2VfY291bnRzKSA8LSBjKCJDb3VudHMiLCAic2xvdCBuYW1lIikKRFQ6OmRhdGF0YWJsZShjaG9vc2VfY291bnRzLCBjYXB0aW9uID0gIkhvdyB0byBzZWxlY3QgeW91ciBjb3VudHMiKQpgYGAKCiMjIEZpbmRNYXJrZXJzCgpXaXRoIHRoZSBmdW5jdGlvbiBbYEZpbmRNYXJrZXJzYF0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9yZWZlcmVuY2UvZmluZG1hcmtlcnMpIApmcm9tIHBhY2thZ2UgYFNldXJhdGAsIHdlIHdhbnQgdG8gbWFrZSB0aHJlZSBncm91cHM6IAoKMS4gT25lIHVzaW5nIGB3aWxjb3hvbmAgdG8gcGVyZm9ybSBERUEgYmV0d2VlbiBjbHVzdGVycyAiOCIgYW5kICIxMCIuCjEuIE9uZSB1c2luZyBgdGAtdGVzdCB0byBwZXJmb3JtIERFQSBiZXR3ZWVuIGNsdXN0ZXJzICI4IiBhbmQgIjEwIi4KMS4gT25lIHVzaW5nIGBNQVNUYCB0byBwZXJmb3JtIERFQSBiZXR3ZWVuICI4IiBhbmQgIjEwIi4KCldlIHdpbGwgb2JzZXJ2ZSB0aGUgcmVzdWx0cyBhbmQgY29tcGFyZSBvdXIgZ2VuZSBsaXN0cy4KCkhleSwgd2h5IGFyZSB5b3UgbG9va2luZyBhdCBtZT8gSXQncyB5b3VyIHR1cm4gdG8gd29yayEgVXNlIHRoZSBhbGwgdGhlCm5vdGlvbnMgc2VlbiBhYm92ZSB0byBzZWxlY3QgdGhlIHJpZ2h0IGNvdW50cyAoYHNsb3RgKSwgdGhlIHJpZ2h0IGlucHV0Cm9iamVjdCwgYW5kIHRoZSByaWdodCBhcmd1bWVudHMuCgoxMCBtaW51dGVzIHByYWN0aWNlICEKCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2Vyczwvc3VtbWFyeT4KCkhlcmUgYXJlIHRoZSBjb2RlIGZvciBlYWNoIHRlYW06CgpgYGB7ciBmaW5kbWFya2Vyc19hbGxfZGUsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQpzb2JqX3dpbGNveCA8LSBTZXVyYXQ6OkZpbmRNYXJrZXJzKAogICMgVGhlIHZhcmlhYmxlIHRoYXQgY29udGFpbnMgU2V1cmF0IE9iamVjdAogIG9iamVjdCA9IHNvYmosCiAgIyBOYW1lIG9mIGNvbmRpdGlvbiAxCiAgaWRlbnQuMSA9ICI4IiwKICAjIE5hbWUgb2YgY29uZGl0aW9uIDIKICBpZGVudC4yID0gIjEwIiwKICAjIEZhY3RvciBuYW1lIGluIHRoZSBTZXVyYXQgT2JqZWN0CiAgZ3JvdXAuYnkgPSAiSGFybW9ueVN0YW5kYWxvbmVfY2x1c3RlcnMiLAogICMgRGlmZmVyZW50aWFsIGFuYWx5c2lzIG1ldGhvZAogIHRlc3QudXNlID0gIndpbGNveCIKKQoKc29ial90IDwtIFNldXJhdDo6RmluZE1hcmtlcnMoCiAgIyBUaGUgdmFyaWFibGUgdGhhdCBjb250YWlucyBTZXVyYXQgT2JqZWN0CiAgb2JqZWN0ID0gc29iaiwKICAjIE5hbWUgb2YgY29uZGl0aW9uIDEKICBpZGVudC4xID0gIjgiLAogICMgTmFtZSBvZiBjb25kaXRpb24gMgogIGlkZW50LjIgPSAiMTAiLAogICMgRmFjdG9yIG5hbWUgaW4gdGhlIFNldXJhdCBPYmplY3QKICBncm91cC5ieSA9ICJIYXJtb255U3RhbmRhbG9uZV9jbHVzdGVycyIsCiAgIyBEaWZmZXJlbnRpYWwgYW5hbHlzaXMgbWV0aG9kCiAgdGVzdC51c2UgPSAidCIKKQoKc29ial9tYXN0IDwtIFNldXJhdDo6RmluZE1hcmtlcnMoCiAgIyBUaGUgdmFyaWFibGUgdGhhdCBjb250YWlucyBTZXVyYXQgT2JqZWN0CiAgb2JqZWN0ID0gc29iaiwKICAjIE5hbWUgb2YgY29uZGl0aW9uIDEKICBpZGVudC4xID0gIjgiLAogICMgTmFtZSBvZiBjb25kaXRpb24gMgogIGlkZW50LjIgPSAiMTAiLAogICMgRmFjdG9yIG5hbWUgaW4gdGhlIFNldXJhdCBPYmplY3QKICBncm91cC5ieSA9ICJIYXJtb255U3RhbmRhbG9uZV9jbHVzdGVycyIsCiAgIyBEaWZmZXJlbnRpYWwgYW5hbHlzaXMgbWV0aG9kCiAgdGVzdC51c2UgPSAiTUFTVCIKKQpgYGAKCmBgYHtyIGxvYWRfZGVhLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CmJhc2U6OnNhdmVSRFMoZmlsZT0ic29ial93aWxjb3guUkRTIiwgb2JqZWN0PXNvYmpfd2lsY294KQpgYGAKCjwvZGV0YWlscz4KPGJyIC8+CgpJbiB0aGUgZnVuY3Rpb24gYXJndW1lbnQsIHRoZXJlIGlzIGEgRm9sZENoYW5nZSB0aHJlc2hvbGQuIFNob3VsZCB3ZQpmaWx0ZXIgZ2VuZSBleHByZXNzaW9uIGJhc2VkIG9uIEZvbGRDaGFuZ2U/IEluIGNhc2Ugb2YgcG9zaXRpdmUgYW5zd2VyLApob3cgbXVjaCBzaG91bGQgdGhhdCB0aHJlc2hvbGQgYmU/Cgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcjwvc3VtbWFyeT4KCgpBYm91dCB0aHJlc2hvbGRzIG9uIEZEUiAoRmFsc2UgRGlzY292ZXJ5IFJhdGUpIGFuZCBMb2cyKEZDKSAoTG9nIG9mIHRoZSBGb2xkIENoYW5nZSksIHRoZXJlIGFyZSBtYW55IGRpc2N1c3Npb25zLgoKUmVtZW1iZXIsIGhlcmUgdGhlIHRocmVzaG9sZCBvbiBGb2xkIENoYW5nZSBpcyBMb2dnZWQuIEEgYGxvZzIoMSkgPSBgYHIgbG9nMigxKWAuIEFuZCBrZWVwIGluIG1pbmQgdGhlIGZvbGxvd2luZzoKCjEuIElmIG9uZSBzZWxlY3RzIGEgZm9sZCBjaGFuZ2UgdGhyZXNob2xkIGFib3ZlIDEuNywgdGhlbiB0aGVpciBzdHVkeSBjb25jbHVkZXMgdGhhdCBzbW9raW5nIGlzIG5vdCByZWxhdGVkIHRvIGx1bmcgY2FuY2VyLgoxLiBJZiBvbmUgc2VsZWN0cyBhIGZvbGQgY2hhbmdlIHRocmVzaG9sZCBhYm92ZSAxLCB0aGVuIHRoZWlyIHN0dWR5IGNvbmNsdWRlcyB0aGF0IGEgZmFzdC1mb29kIGJhc2VkIGRpZXQgZG9lcyBub3QgbGVhZCB0byB3ZWlnaHQgZ2Fpbi4KMS4gSWYgb25lIHNlbGVjdHMgYSBmb2xkIGNoYW5nZSB0aHJlc2hvbGQgYWJvdmUgMS8yNSAwMDAgMDAwLCB0aGVuIHRoZWlyIHN0dWR5IGNvbmNsdWRlczogaXQgaXMgYSBjb21wbGV0ZSBoYXphcmQgdGhhdCBtaWNlIGhhdmUgZmV0YWwgbWFsZm9ybWF0aW9uIHdoZW4gaW4gcHJlc2VuY2Ugb2YgQmlzcGhhbm9sLgoKSW4gY29uY2x1c2lvbiwgdGhlcmUgb25lLCBhbmQgb25seSBvbmUgcmVhc29uIHRvIGZpbHRlciBvbiBmb2xkIGNoYW5nZTogaW4gbXkgZXhwZXJpZW5jZSwgYSBmb2xkIGNoYW5nZSBiZWxvdyAwLjcgd2lsbCBiZSBoYXJkIHRvIHNlZS92ZXJpZnkgb24gd2V0LWxhYiAocVJUKS4KCklmIHlvdSBuZWVkIHRvIHJlZHVjZSBhIHRvbyBsYXJnZSBudW1iZXIgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLCB0aGVuIHJlZHVjZSB0aGUgRkRSIHRvIDAuMDEsIG9yIGV2ZW4gYmV0dGVyLCB0byAwLjAwMS4gV2l0aCB0aGF0LCB5b3UgcmVkdWNlIHlvdXIgbnVtYmVyIG9mIGZhbHNlIGNsYWltcy4KCjwvZGV0YWlscz4KPGJyIC8+CgpDYW4geW91IGhlbHAgbWUgd2l0aCBgREVzZXEyYD8KCldoZW4gSSBydW4gdGhlIGZvbGxvd2luZyBjb21tYW5kIGxpbmUsIEkgaGF2ZSBhbiBlcnJvciA6CgpgYGB7ciBzZXVyYXRfcnVuX2Rlc2VxX3dpdGhfZXJyb3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0Kc29ial9kZXNlcTIgPC0gU2V1cmF0OjpGaW5kTWFya2VycygKICAjIFRoZSB2YXJpYWJsZSB0aGF0IGNvbnRhaW5zIFNldXJhdCBPYmplY3QKICBvYmplY3QgPSBzb2JqLAogICMgTmFtZSBvZiBjb25kaXRpb24gMQogIGlkZW50LjEgPSA4LAogICMgTmFtZSBvZiBjb25kaXRpb24gMgogIGlkZW50LjIgPSAxMCwKICAjIEZhY3RvciBuYW1lIGluIHRoZSBTZXVyYXQgT2JqZWN0CiAgZ3JvdXAuYnkgPSAiSGFybW9ueVN0YW5kYWxvbmVfY2x1c3RlcnMiLAogICMgRGlmZmVyZW50aWFsIGFuYWx5c2lzIG1ldGhvZAogIHRlc3QudXNlID0gImRlc2VxMiIsCiAgIyBVc2Ugbm9uLW5vcm1hbGl6ZWQgZGF0YSB3aXRoIERFU2VxMgogIHNsb3QgPSAiY291bnRzIgopCmBgYAoKPiBFcnJvciBpbiBQZXJmb3JtREUob2JqZWN0ID0gb2JqZWN0LCBjZWxscy4xID0gY2VsbHMuMSwgY2VsbHMuMiA9IGNlbGxzLjIsICA6IAo+ICAgVW5rbm93biB0ZXN0OiBkZXNlcTIKCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcjwvc3VtbWFyeT4KCk9oISBNeSBmYXVsdCwgaXQgd2FzIGEgdHlwbyBpbiBteSBjb21tYW5kISBUaGFuayB5b3UgYWxsIGZvciB5b3VyIGhlbHAhCgpgYGB7ciBidWlsZF9jb3VudF9wMV9tYXRyaXgsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQpzb2JqX2Rlc2VxMiA8LSBTZXVyYXQ6OkZpbmRNYXJrZXJzKAogICMgVGhlIHZhcmlhYmxlIHRoYXQgY29udGFpbnMgU2V1cmF0IE9iamVjdAogIG9iamVjdCA9IHNvYmosCiAgIyBOYW1lIG9mIGNvbmRpdGlvbiAxCiAgaWRlbnQuMSA9ICI4IiwKICAjIE5hbWUgb2YgY29uZGl0aW9uIDIKICBpZGVudC4yID0gIjEwIiwKICAjIEZhY3RvciBuYW1lIGluIHRoZSBTZXVyYXQgT2JqZWN0CiAgZ3JvdXAuYnkgPSAiSGFybW9ueVN0YW5kYWxvbmVfY2x1c3RlcnMiLAogICMgRGlmZmVyZW50aWFsIGFuYWx5c2lzIG1ldGhvZAogIHRlc3QudXNlID0gIkRFU2VxMiIsCiAgIyBVc2Ugbm9uLW5vcm1hbGl6ZWQgZGF0YSB3aXRoIERFU2VxMgogIHNsb3QgPSAiY291bnRzIgopCmBgYAoKUmVtYXJrOiBieSBkb2luZyBzdXJjaCBtb2RpZmljYXRpb24sIHNvbWUgZm9sZCBjaGFuZ2VzIGhhdmUgYmVlbiBtb2RpZmllZDoKcmVtZW1iZXIgdGhlIGdlbmUgQXRhZDIgd2l0aCBhIG1lYW4gZXhwcmVzc2lvbiBvZiAwLjA4IGluIEcxLCBhbmQgMC4yIGluIFMgCnBoYXNlcz8gTWVhbiBleHByZXNzaW9ucyBhcmUgbm93IGFyb3VuZCAxLjA4IGZvciBHMSwgYW5kIDEuMiBmb3IgUyBwaGFzZXMuClRoaXMgbWF5IGJlIHRoZSByZWFzb24gd2h5IGl0IHdhcyBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIERFU2VxMiwKd2hpbGUgV2lsY294b24gYW5kIFQtdGVzdCByZXR1cm5lZCBhZGp1c3RlZCBwdmFsdWVzIGZhciBiZWxvdyAwLjA1LgoKPC9kZXRhaWxzPgoKCgpgYGB7ciBzYXZlX2RlX3Jlc3VsdHMsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KYmFzZTo6c2F2ZVJEUyhzb2JqX3dpbGNveCwgInNvYmpfd2lsY294LlJEUyIpCmJhc2U6OnNhdmVSRFMoc29ial90LCAic29ial90LlJEUyIpCmJhc2U6OnNhdmVSRFMoc29ial9kZXNlcTIsICJzb2JqX2Rlc2VxMi5SRFMiKQpgYGAKCiMgRXhwbG9yZSByZXN1bHRzCgojIyBCaWcgdGFibGVzCgpMZXQgdXMgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlc3VsdHM6CgpgYGB7ciBzb2JqX3dfcmVzX2Rpc3BsYXksIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KRFQ6OmRhdGF0YWJsZSgKICBoZWFkKHNvYmpfd2lsY294LCBuID0gMTApLAogIGNhcHRpb24gPSAiV2lsY294b24gdGVzdCByZXN1bHRzIgopCmBgYAoKV2UgaGF2ZSB0aGUgZm9sbG93aW5nIGNvbHVtbnM6CgoxLiBgcF92YWxgOiBJZ25vcmUgdGhpcyBjb2x1bW4uIEFsd2F5cyBpZ25vcmUgcmF3IHAtdmFsdWVzLiBMb29rIGF0IGNvcnJlY3RlZCBvbmVzLCBhbmQgaWYgdGhleSBhcmUgbWlzc2luZywgdGhlbiBjb21wdXRlIHRoZW0uCjEuIGBhdmdfbG9nMkZDYDogQXZlcmFnZSBMb2cyKEZvbGRDaGFuZ2UpLiBJbGx1c3RyYXRlcyBob3cgbXVjaCBhIGdlbmUgaXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gc2FtcGxlcyBpbiBlYWNoIGNvbmRpdGlvbi4KMS4gYHBjdC4xYDogUGVyY2VudCBvZiBjZWxscyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBpbiBjb25kaXRpb24gb25lLCBoZXJlIGluIGNsdXN0ZXIgOC4KMS4gYHBjdC4yYDogUGVyY2VudCBvZiBjZWxscyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBpbiBjb25kaXRpb24gdHdvLCBoZXJlIGluIGNsdXN0ZXIgMTAuCjEuIGBwX3ZhbF9hZGpgOiBUaGUgY29uZmlkZW5jZSB3ZSBoYXZlIGluIHRoZSByZXN1bHQuIFRoZSBjbG9zZXIgdG8gMCwgdGhlIGxlc3NlciBpcyB0aGUgcmlzayBvZiBlcnJvci4KCgpgYGB7ciBzb2JqX3RfcmVzX2Rpc3BsYXksIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KRFQ6OmRhdGF0YWJsZSgKICBoZWFkKHNvYmpfdCwgbiA9IDEwKSwKICBjYXB0aW9uID0gIlQtdGVzdCByZXN1bHRzIgopCmBgYAoKV2UgaGF2ZSB0aGUgZm9sbG93aW5nIGNvbHVtbnM6CgoxLiBgcF92YWxgOiBJZ25vcmUgdGhpcyBjb2x1bW4uIEFsd2F5cyBpZ25vcmUgcmF3IHAtdmFsdWVzLiBMb29rIGF0IGNvcnJlY3RlZCBvbmVzLCBhbmQgaWYgdGhleSBhcmUgbWlzc2luZywgdGhlbiBjb21wdXRlIHRoZW0uCjEuIGBhdmdfbG9nMkZDYDogQXZlcmFnZSBMb2cyKEZvbGRDaGFuZ2UpLiBJbGx1c3RyYXRlcyBob3cgbXVjaCBhIGdlbmUgaXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gc2FtcGxlcyBpbiBlYWNoIGNvbmRpdGlvbi4KMS4gYHBjdC4xYDogUGVyY2VudCBvZiBjZWxscyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBpbiBjb25kaXRpb24gb25lLCBoZXJlIGluIGNsdXN0ZXIgOC4KMS4gYHBjdC4yYDogUGVyY2VudCBvZiBjZWxscyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBpbiBjb25kaXRpb24gdHdvLCBoZXJlIGluIGNsdXN0ZXIgMTAuCjEuIGBwX3ZhbF9hZGpgOiBUaGUgY29uZmlkZW5jZSB3ZSBoYXZlIGluIHRoZSByZXN1bHQuIFRoZSBjbG9zZXIgdG8gMCwgdGhlIGxlc3NlciBpcyB0aGUgcmlzayBvZiBlcnJvci4KCmBgYHtyIHNvYmpfbWFzdF9yZXNfZGlzcGxheSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpEVDo6ZGF0YXRhYmxlKAogIGhlYWQoc29ial9tYXN0LCBuID0gMTApLAogIGNhcHRpb24gPSAiTUFTVCB0ZXN0IHJlc3VsdHMiCikKYGBgCgpXZSBoYXZlIHRoZSBmb2xsb3dpbmcgY29sdW1uczoKCjEuIGBwX3ZhbGA6IElnbm9yZSB0aGlzIGNvbHVtbi4gQWx3YXlzIGlnbm9yZSByYXcgcC12YWx1ZXMuIExvb2sgYXQgY29ycmVjdGVkIG9uZXMsIGFuZCBpZiB0aGV5IGFyZSBtaXNzaW5nLCB0aGVuIGNvbXB1dGUgdGhlbS4KMS4gYGF2Z19sb2cyRkNgOiBBdmVyYWdlIExvZzIoRm9sZENoYW5nZSkuIElsbHVzdHJhdGVzIGhvdyBtdWNoIGEgZ2VuZSBpcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBzYW1wbGVzIGluIGVhY2ggY29uZGl0aW9uLgoxLiBgcGN0LjFgOiBQZXJjZW50IG9mIGNlbGxzIHdpdGggZ2VuZSBleHByZXNzaW9uIGluIGNvbmRpdGlvbiBvbmUsIGhlcmUgaW4gY2x1c3RlciA4LgoxLiBgcGN0LjJgOiBQZXJjZW50IG9mIGNlbGxzIHdpdGggZ2VuZSBleHByZXNzaW9uIGluIGNvbmRpdGlvbiB0d28sIGhlcmUgaW4gY2x1c3RlciAxMC4KMS4gYHBfdmFsX2FkamA6IFRoZSBjb25maWRlbmNlIHdlIGhhdmUgaW4gdGhlIHJlc3VsdC4gVGhlIGNsb3NlciB0byAwLCB0aGUgbGVzc2VyIGlzIHRoZSByaXNrIG9mIGVycm9yLgoKCmBgYHtyIHNvYmpfZGVzZXFfcmVzX2Rpc3BsYXksIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KRFQ6OmRhdGF0YWJsZSgKICBoZWFkKHNvYmpfdCwgbiA9IDEwKSwKICBjYXB0aW9uID0gIlQtdGVzdCByZXN1bHRzIgopCmBgYAoKV2UgaGF2ZSB0aGUgZm9sbG93aW5nIGNvbHVtbnM6CgoxLiBgcF92YWxgOiBJZ25vcmUgdGhpcyBjb2x1bW4uIEFsd2F5cyBpZ25vcmUgcmF3IHAtdmFsdWVzLiBMb29rIGF0IGNvcnJlY3RlZCBvbmVzLCBhbmQgaWYgdGhleSBhcmUgbWlzc2luZywgdGhlbiBjb21wdXRlIHRoZW0uCjEuIGBhdmdfbG9nMkZDYDogQXZlcmFnZSBMb2cyKEZvbGRDaGFuZ2UpLiBJbGx1c3RyYXRlcyBob3cgbXVjaCBhIGdlbmUgaXMgZGlmZmVyZW50aWFsbHkgZXhwZXNzZWQgYmV0d2VlbiBzYW1wbGVzIGluIGVhY2ggY29uZGl0aW9uLgoxLiBgcGN0LjFgOiBQZXJjZW50IG9mIGNlbGxzIHdpdGggZ2VuZSBleHByZXNzaW9uIGluIGNvbmRpdGlvbiBvbmUsIGhlcmUgaW4gY2x1c3RlciA4LgoxLiBgcGN0LjJgOiBQZXJjZW50IG9mIGNlbGxzIHdpdGggZ2VuZSBleHByZXNzaW9uIGluIGNvbmRpdGlvbiB0d28sIGhlcmUgaW4gY2x1c3RlciAxMC4KMS4gYHBfdmFsX2FkamA6IFRoZSBjb25maWRlbmNlIHdlIGhhdmUgaW4gdGhlIHJlc3VsdC4gVGhlIGNsb3NlciB0byAwLCB0aGUgbGVzc2VyIGlzIHRoZSByaXNrIG9mIGVycm9yLgoKCiMjIEZpbHRlciBERUEgcmVzdWx0cwoKV2hhdCBraW5kIG9mIHRocmVzaG9sZCBzaG91bGQgYmUgdXNlZCB0byBmaWx0ZXIgZWFjaCByZXN1bHRzPwoKYGBge3IgZXh0cmFjdF9kZV9nZW5lcywgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQojIFdlIHN0b3JlIGEgYGxpc3RgIGluIGEgdmFyaWFibGUgY2FsbGVkIGBkYXRhYAojIFRoZSBmdW5jdGlvbiBgbGlzdGAgY29tZXMgZnJvbSBgYmFzZWAgYW5kIG5vdCBgYmlvY0dlbmVyaWNzYC4KZGF0YSA8LSBiYXNlOjpsaXN0KAogICMgV2UgdXNlIGEgdGhyZXNob2xkIG9mIDUlIG9uIGFkanVzdGVkIHAtdmFsdWVzCiAgd2lsY294ID0gYmFzZTo6cm93bmFtZXMoc29ial93aWxjb3hbc29ial93aWxjb3gkcF92YWxfYWRqIDw9IDAuMDUsIF0pLAogICMgV2UgdXNlIGEgdGhyZXNob2xkIG9mIDUlIG9uIGFkanVzdGVkIHAtdmFsdWVzCiAgdF90ZXN0ID0gYmFzZTo6cm93bmFtZXMoc29ial90W3NvYmpfdCRwX3ZhbF9hZGogPD0gMC4wNSwgXSksCiAgIyBXZSB1c2UgYSB0aHJlc2hvbGQgb2YgMC4yIGluIEFVQyBwb3dlcgogIG1hc3QgICAgPSBiYXNlOjpyb3duYW1lcyhzb2JqX21hc3Rbc29ial9tYXN0JHBfdmFsX2FkaiA8PSAwLjA1LCBdKSwKICAjIFdlIHVzZSBhIHRocmVzaG9sZCBvZiA1JSBvbiBhZGp1c3RlZCBwLXZhbHVlcwogIGRlc2VxMiA9IGJhc2U6OnJvd25hbWVzKHNvYmpfZGVzZXEyW3NvYmpfZGVzZXEyJHBfdmFsX2FkaiA8PSAwLjA1LCBdKQopCmBgYAoKPiBJZiB3ZSBtdXN0IGxhYmVsIGNlcnRhaW4gc2NvcmVzIGFzIGdvb2Qgb3IgYmFkLCB3ZSBjYW4gcmVmZXJlbmNlIHRoZSAKZm9sbG93aW5nIHJ1bGUgb2YgdGh1bWI6Cj4KPiAwLjUgPSBObyBkaXNjcmltaW5hdGlvbgo+IDAuNS0wLjcgPSBQb29yIGRpc2NyaW1pbmF0aW9uCj4gMC43LTAuOCA9IEFjY2VwdGFibGUgZGlzY3JpbWluYXRpb24KPiAwLjgtMC45PSBFeGNlbGxlbnQgZGlzY3JpbWluYXRpb24KPiAwLjkgPSBPdXRzdGFuZGluZyBkaXNjcmltaW5hdGlvbgoKSG9zbWVyIGFuZCBMZW1lc2hvdyBpbiBBcHBsaWVkIExvZ2lzdGljIFJlZ3Jlc3Npb24gKHAuIDE3NykKCiMjIEFkZCByZXN1bHRzIHRvIFNldXJhdCBvYmplY3RzCgpXZSdkIGxpa2UgdG8gc3RvcmUgdGhlIHJlc3VsdHMgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgaW4gCnRoZSBgU2V1cmF0YCBvYmplY3QuCgpgYGB7ciBhZGRfcmVzdWx0c19zZXVyYXQsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQpzb2JqQG1pc2Mkd2lsY294IDwtIHNvYmpfd2lsY294CmBgYAoKYGBge3Igc2F2ZV9zb2JqdywgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpiYXNlOjpzYXZlUkRTKHNvYmosICJERUFfU2NhbGVkX05vcm1hbGl6ZWRfRmlsdGVyZWQuUkRTIikKYGBgCgojIyBDb21tb24gcmVzdWx0cwoKTm93IHdlIGNhbiBwbG90IGludGVyc2VjdGlvbnMgaW4gYW4gdXAtc2V0IGdyYXBoLiBJdCBpcyBsaWtlIGEgVmVubiBkaWFncmFtOgoKYGBge3IgdXBzZXRfc2V1cmF0X2RlX21ldGhvZHMsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQpVcFNldFI6OnVwc2V0KAogIGRhdGEgPSBVcFNldFI6OmZyb21MaXN0KGRhdGEpLAogIG9yZGVyLmJ5ID0gImZyZXEiCikKYGBgCgojIyBPdGhlcnMgZnVuY3Rpb25zIGluIFNldXJhdCBmb3IgREVBCgpUaGVyZSBhcmUgdHdvIG90aGVyIGZ1bmN0aW9ucyBpbiBTZXVyYXQgcGFja2FnZSB0byBkbyBERUEgaW4gZGlmZmVyZW50IGNvbnRleHQuIEZvciBleGFtcGxlLCB5b3Ugd2lsbCBzZWUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBvbmUgY2x1c3RlciB2ZXJzdXMgbWFueSBvZiB0aGVtLiBZb3UgY291bGQgYmUgdXNlIGBGaW5kQWxsTWFya2Vyc2AuIFVzYWdlIGlzIGVxdWl2YWxlbnQgdG8gYEZpbmRNYXJrZXJzYCBidXQgaXQgbmVlZCB0byBpbmRpY2F0ZSB3aGljaCB2YXJpYWJsZSBjbHVzdGVyIHZhcmlhYmxlLgoKYGBge3IgZmluZGFsbG1hcmtlcnNfZGVhfQoKc29ial9maW5kYWxsbWFya2Vyc19tYXN0IDwtIEZpbmRBbGxNYXJrZXJzKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheT0iUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5PSJIYXJtb255U3RhbmRhbG9uZV9jbHVzdGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LnVzZSA9ICJNQVNUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuOSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wY3QgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IG15X3NlZWQpCgpEVDo6ZGF0YXRhYmxlKAogIGhlYWQoc29ial9maW5kYWxsbWFya2Vyc19tYXN0LCBuID0gMTApLAogIGNhcHRpb24gPSAiTUFTVCByZXN1bHRzIgopCgoKcCA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgdG9wdGFibGUgPSBzb2JqX2ZpbmRhbGxtYXJrZXJzX21hc3QsCiAgbGFiID0gcm93bmFtZXMoc29ial9maW5kYWxsbWFya2Vyc19tYXN0KSwKICB4ID0gImF2Z19sb2cyRkMiLAogIHkgPSAicF92YWxfYWRqIiwKICBGQ2N1dG9mZiA9IDAuMiwKICBkcmF3Q29ubmVjdG9ycyA9IEZBTFNFLAogIHRpdGxlID0gJ1ZvbGNhbm8gYnkgY2x1c3RlcicKKQoKcCArIGZhY2V0X3dyYXAofiBjbHVzdGVyKQoKYGBgCgpBbm90aGVyIGZ1bmN0aW9uIGlzIGBGaW5kQ29uc2VydmVkTWFya2Vyc2AuIEl0IHRoZSBzYW1lIGZ1bmN0aW9uIG9mIGBGaW5kTWFya2Vyc2AgYnV0IHRoaXMgdXNhZ2UgaXMgdG8gaWRlbnRpZnkgdGhlIHNhbWUgZmVhdHVyZXMgZXhwcmVzc2lvbiBiZXR3ZWVuIGNsdXN0ZXJzCgpgYGB7ciBmaW5kY29uc2VydmVkbWFya2Vyc19kZWF9CnNvYmpfY29uc2VydmVkbWFya2Vyc19tYXN0IDwtIFNldXJhdDo6RmluZENvbnNlcnZlZE1hcmtlcnMoCiAgIyBUaGUgdmFyaWFibGUgdGhhdCBjb250YWlucyBTZXVyYXQgT2JqZWN0CiAgb2JqZWN0ID0gc29iaiwKICAjIE5hbWUgb2YgY29uZGl0aW9uIDEKICBpZGVudC4xID0gIjgiLAogICMgTmFtZSBvZiBjb25kaXRpb24gMgogIGlkZW50LjIgPSAiMTAiLAogICMgRmFjdG9yIG5hbWUgaW4gdGhlIFNldXJhdCBPYmplY3QKICBncm91cGluZy52YXIgPSAib3JpZy5pZGVudCIsCiAgIyBEaWZmZXJlbnRpYWwgYW5hbHlzaXMgbWV0aG9kCiAgYXNzYXk9IlJOQSIsCiAgc2xvdD0iY291bnRzIgopCgpEVDo6ZGF0YXRhYmxlKAogIGhlYWQoc29ial9jb25zZXJ2ZWRtYXJrZXJzX21hc3QsIG4gPSAxMCksCiAgY2FwdGlvbiA9ICJNQVNUIHJlc3VsdHMiCikKYGBgIAoKMS4gYFREQ1RfcF92YWxgOiBJZ25vcmUgdGhpcyBjb2x1bW4uIEFsd2F5cyBpZ25vcmUgcmF3IHAtdmFsdWVzLiBMb29rIGF0IGNvcnJlY3RlZCBvbmVzLCBhbmQgaWYgdGhleSBhcmUgbWlzc2luZywgdGhlbiBjb21wdXRlIHRoZW0uCjEuIGBURENUX2F2Z19sb2cyRkNgOiBBdmVyYWdlIExvZzIoRm9sZENoYW5nZSkuIElsbHVzdHJhdGVzIGhvdyBtdWNoIGEgZ2VuZSBpcyBkaWZmZXJlbnRpYWxseSBleHBlc3NlZCBiZXR3ZWVuIHNhbXBsZXMgaW4gVERDVCBjb25kaXRpb24uCjEuIGBURENUX3BjdC4xYDogUGVyY2VudCBvZiBjZWxscyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBpbiBjb25kaXRpb24gb25lLCBoZXJlIGluIGNsdXN0ZXIgOCBpbiBURENUIGNvbmRpdGlvbi4KMS4gYFREQ1RfcGN0LjJgOiBQZXJjZW50IG9mIGNlbGxzIHdpdGggZ2VuZSBleHByZXNzaW9uIGluIGNvbmRpdGlvbiB0d28sIGhlcmUgaW4gY2x1c3RlciAxMCBpbiBURENUIGNvbmRpdGlvbi4KMS4gYFREQ1RfcF92YWxfYWRqYDogVGhlIGNvbmZpZGVuY2Ugd2UgaGF2ZSBpbiB0aGUgcmVzdWx0LiBUaGUgY2xvc2VyIHRvIDAsIHRoZSBsZXNzZXIgaXMgdGhlIHJpc2sgb2YgZXJyb3IgaW4gVERDVCBjb25kaXRpb24uCgoxLiBgVEQzQV9wX3ZhbGA6IElnbm9yZSB0aGlzIGNvbHVtbi4gQWx3YXlzIGlnbm9yZSByYXcgcC12YWx1ZXMuIExvb2sgYXQgY29ycmVjdGVkIG9uZXMsIGFuZCBpZiB0aGV5IGFyZSBtaXNzaW5nLCB0aGVuIGNvbXB1dGUgdGhlbS4KMS4gYFREM0FfYXZnX2xvZzJGQ2A6IEF2ZXJhZ2UgTG9nMihGb2xkQ2hhbmdlKS4gSWxsdXN0cmF0ZXMgaG93IG11Y2ggYSBnZW5lIGlzIGRpZmZlcmVudGlhbGx5IGV4cGVzc2VkIGJldHdlZW4gc2FtcGxlcyBpbiBURDNBIGNvbmRpdGlvbi4KMS4gYFREM0FfcGN0LjFgOiBQZXJjZW50IG9mIGNlbGxzIHdpdGggZ2VuZSBleHByZXNzaW9uIGluIGNvbmRpdGlvbiBvbmUsIGhlcmUgaW4gY2x1c3RlciA4IGluIFREM0EgY29uZGl0aW9uLgoxLiBgVEQzQV9wY3QuMmA6IFBlcmNlbnQgb2YgY2VsbHMgd2l0aCBnZW5lIGV4cHJlc3Npb24gaW4gY29uZGl0aW9uIHR3bywgaGVyZSBpbiBjbHVzdGVyIDEwIGluIFREM0EgY29uZGl0aW9uLgoxLiBgVEQzQV9wX3ZhbF9hZGpgOiBUaGUgY29uZmlkZW5jZSB3ZSBoYXZlIGluIHRoZSByZXN1bHQuIFRoZSBjbG9zZXIgdG8gMCwgdGhlIGxlc3NlciBpcyB0aGUgcmlzayBvZiBlcnJvciBpbiBURDNBIGNvbmRpdGlvbi4KCjEuIGBtYXhfcHZhbGA6ICBNYXhpbXVtIG9mIHBfdmFsIGJldHdlZW4gY29tcGFyaXNvbiBURENUIHZzIFREM0EuIEl0IG11c3QgYmUgbG93IHRvIGluZGljYXRlIGEgY29uc2VydmVkIGZlYXR1cmUKMS4gYG1pbmltdW1wX3BfdmFsYDogTWluaW11bSBvZiBwX3ZhbCBiZXR3ZWVuIGNvbXBhcmlzb24gVERDVCB2cyBURDNBLgoKYGBge3Igdm9sY2Fub19wbGFjOCBfYnlfc2FtcGxlfQpTZXVyYXQ6OlZsblBsb3QoCiAgIyBBIHN1YnNldCBvZiB0aGUgU2V1cmF0IG9iamVjdAogICMgbGltaXRlZCB0byBjbHVzdGVycyA4IGFuZCAxMCwgCiAgIyBvciBlbHNlIHdlIHdpbGwgcGxvdCBhbGwgdGhlIGNsdXN0ZXJzCiAgb2JqZWN0ID0gc3Vic2V0KHNvYmosIEhhcm1vbnlTdGFuZGFsb25lX2NsdXN0ZXJzICVpbiUgYygiOCIsICIxMCIpKSwKICAKICAjIFRoZSBuYW1lIG9mIHRoZSBnZW5lIG9mIGludGVyZXN0IChmZWF0dXJlID0gZ2VuZSkKICBmZWF0dXJlcyA9ICJQbGFjOCIsCiAgCiAgIyBUaGUgbmFtZSBvZiB0aGUgU2V1cmF0IGNlbGwgYW5ub3RhdGlvbgogIHNwbGl0LmJ5ID0gIm9yaWcuaWRlbnQiLAogIAogICMgQ2hhbmdlIGNvbG9yIGZvciBwcmVzZW50YXRpb24KICBjb2xzID0gYygiYmx1ZSIsICJyZWQiKQogIAopCgpzb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdCRkaXJlY3Rpb24gPSBjYXNlX3doZW4oCiAgc2lnbihzb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdCRURENUX2F2Z19sb2cyRkMpID09IHNpZ24oc29ial9jb25zZXJ2ZWRtYXJrZXJzX21hc3QkVEQzQV9hdmdfbG9nMkZDKSAmCiAgICBzaWduKHNvYmpfY29uc2VydmVkbWFya2Vyc19tYXN0JFREQ1RfYXZnX2xvZzJGQykgPT0gLTEgfiAibmVnYXRpdmUiLAogIHNpZ24oc29ial9jb25zZXJ2ZWRtYXJrZXJzX21hc3QkVERDVF9hdmdfbG9nMkZDKSA9PSBzaWduKHNvYmpfY29uc2VydmVkbWFya2Vyc19tYXN0JFREM0FfYXZnX2xvZzJGQykgJgogICAgc2lnbihzb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdCRURENUX2F2Z19sb2cyRkMpID09IDEgfiAicG9zaXRpdmUiLAogIC5kZWZhdWx0ID0gIm9wcG9zaXRlIikKCgp0X2RhdGFmcmFtZSA8LSB0YWJsZShzb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdCRkaXJlY3Rpb24sc29ial9jb25zZXJ2ZWRtYXJrZXJzX21hc3QkbWF4X3B2YWw8MC4wNSlbLDJdCgpsZXZlbHNfZGlyZWN0aW9uIDwtIHBhc3RlMChuYW1lcyh0X2RhdGFmcmFtZSksIiAobkZlYXR1cmU9Iix0X2RhdGFmcmFtZSwiKSIpCgpzb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdF9maWx0ZXJlZCA9IHNvYmpfY29uc2VydmVkbWFya2Vyc19tYXN0W3NvYmpfY29uc2VydmVkbWFya2Vyc19tYXN0JG1pbmltdW1wX3BfdmFsPDAuMDUsXQpzb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdF9maWx0ZXJlZCRkaXJlY3Rpb24gPSBmYWN0b3Ioc29ial9jb25zZXJ2ZWRtYXJrZXJzX21hc3RfZmlsdGVyZWQkZGlyZWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCJuZWdhdGl2ZSIsIm9wcG9zaXRlIiwicG9zaXRpdmUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9bGV2ZWxzX2RpcmVjdGlvbikgIAoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1zb2JqX2NvbnNlcnZlZG1hcmtlcnNfbWFzdF9maWx0ZXJlZCwKICAgICAgICAgICAgIGFlcyh4ID0gVERDVF9hdmdfbG9nMkZDLCB5ID0gVEQzQV9hdmdfbG9nMkZDLCBjb2xvciA9IGRpcmVjdGlvbikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImxpZ2h0Ymx1ZSIsImdyZXk3MCIsInJlZCIpKSArCgogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICJURENUIEF2ZXJhZ2UgTG9nMiBGb2xkQ2hhbmdlIGNsdXN0ZXIgOCB2cyBjbHVzdGVyIDEyIiwKICAgICAgIHkgPSAiVEQzQSBBdmVyYWdlIExvZzIgRm9sZENoYW5nZSBjbHVzdGVyIDggdnMgY2x1c3RlciAxMiIsCiAgICAgICB0aXRsZSA9ICJSZXN1bHQgb2YgV2lsY294b24gVGVzdCBiZXR3ZWVuIGNsdXN0ZXIgOCB2cyBjbHVzdGVyIDEyIGJldHdlZW4gY29uZGl0aW9uIFREQ1QgdnMgVEQzQSIsCiAgICAgICBjb2xvciA9ICJEaXJlY3Rpb24gTG9nMkZvbGRDaGFuZ2UiKSAKICAgICAgIAoKCmBgYAojIyBIZWF0bWFwCgpXZSdkIGxpa2UgdG8gZGlzcGxheSB0aGUgZXhwcmVzc2lvbiBvZiBnZW5lcyBpZGVudGlmaWVkIGJ5IEZpbmRNYXJrZXJzLiAKVGhlbiB3ZSB1c2UgdGhlIGZ1bmN0aW9uIFtgRG9IZWF0bWFwYF0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9yZWZlcmVuY2UvZG9oZWF0bWFwKSAKZnJvbSB0aGUgcGFja2FnZSBgU2V1cmF0YC4KCkluIG9yZGVyIHRvIGxpbWl0IHRoZSBncmFwaCB0byBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgcmVhZHMsIHdlIHVzZSB0aGUKZnVuY3Rpb24gW2Byb3duYW1lc2BdKGh0dHBzOi8vcmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy42LjIvdG9waWNzL3Jvdy5uYW1lcykKZnJvbSBSIGBiYXNlYCBwYWNrYWdlIG9uIHRoZSBERUEgcmVzdWx0IHRhYmxlLiBJbiB0aGlzIGV4YW1wbGUsIEkgdXNlCnRoZSByZXN1bHRzIG9mIHdpbGNveG9uLCBidXQgeW91IHNoYWxsIHVzZSBhbnkgb2YgdGhlIHJlc3VsdHMgeW91IHByZXZpb3VzbHkKb2J0YWluZWQuCgpgYGB7ciBzZXVyYXRfaGVhdG1hcCwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9ClNldXJhdDo6RG9IZWF0bWFwKAogICMgdmFyaWFibGUgcG9pbnRpbmcgdG8gc2V1cmF0IG9iamVjdAogIG9iamVjdCA9IHNvYmosCiAgIyBuYW1lIG9mIERFIGdlbmVzCiAgZmVhdHVyZXMgPSBiYXNlOjpyb3duYW1lcyhzb2JqX3dpbGNveCksCiAgIyBDbHVzdGVyIGFubm90YXRpb24KICBncm91cC5ieSA9ICJIYXJtb255U3RhbmRhbG9uZV9jbHVzdGVycyIsCikKYGBgCgojIyBWb2xjYW5vIHBsb3QKCkEgVm9sY2FubyBwbG90IGlzIHVzZWZ1bGwgdG8gaWRlbnRpZnkgZGlmZmVybmV0aWFsIGV4cHJlc3Npb24KYW5hbHlzaXMgYmlhcy4KClRoZSBwYWNrYWdlIGBFbmhhbmNlZFZvbGNhbm9gIGhhcyBhbiBbZXBvbnltXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xNy9iaW9jL2h0bWwvRW5oYW5jZWRWb2xjYW5vLmh0bWwpCmZ1bmN0aW9uIGZvciB0aGF0OgoKYGBge3IgZW5oYW5jZWRfdm9sY2Fub3Bsb3QsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQpFbmhhbmNlZFZvbGNhbm86OkVuaGFuY2VkVm9sY2FubygKICAjICB2YXJpYWJsZSBwb2ludGluZyB0byB0aGUgREVBIHJlc3VsdHMKICB0b3B0YWJsZSA9IHNvYmpfd2lsY294LAogICMgR2VuZSBuYW1lcwogIGxhYiA9IHJvd25hbWVzKHNvYmpfd2lsY294KSwKICAjIENvbHVtbiBpbiB3aGljaCB0byBmaW5kIEZvbGQgQ2hhbmdlCiAgeCA9ICJhdmdfbG9nMkZDIiwKICAjIENvbHVtbiBpbiB3aGljaCB0byBmaW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWwKICB5ID0gInBfdmFsX2FkaiIsCiAgIyBMb3dlciBmb2xkLWNoYW5nZSBjdXQtb2ZmCiAgRkNjdXRvZmYgPSAwLjIKKQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKVGhpcyBsaXN0IG9mIGFsbCBwYWNrYWdlcyB1c2VkIHdoaWxlIHlvdSB3b3JrIHNob3VsZCBiZSBpbmNsdWRlZAppbiBlYWNoIGVuIGV2ZXJ5IFIgcHJlc2VudGF0aW9uOgoKYGBge3Igc2Vzc2lvbl9pbmZvLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KdXRpbHM6OnNlc3Npb25JbmZvKCkKYGBgCg==